Ответ
Swizzling — это техника подмены реализации метода во время выполнения. Она возможна благодаря динамической природе Objective-C Runtime, который управляет диспетчеризацией сообщений (вызовов методов).
Основные шаги:
- Получить
Method(структура runtime) для оригинального и нового селекторов. - Добавить новый метод в класс, если его ещё нет.
- Обменять реализации (
IMP) двух методов.
Пример swizzling viewDidLoad:
import ObjectiveC
extension UIViewController {
static func swizzleViewDidLoad() {
// 1. Убедимся, что swizzling выполняется только один раз
guard self === UIViewController.self else { return }
let originalSelector = #selector(viewDidLoad)
let swizzledSelector = #selector(swizzled_viewDidLoad)
// 2. Получаем объекты Method из runtime
guard let originalMethod = class_getInstanceMethod(self, originalSelector),
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
else { return }
// 3. Пытаемся добавить новый метод. Если получилось, значит метод не был унаследован.
let didAddMethod = class_addMethod(self, originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod))
if didAddMethod {
// Метод был добавлен -> подменяем селектор
class_replaceMethod(self, swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod))
} else {
// Метод уже существует -> просто меняем реализации местами
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
@objc private func swizzled_viewDidLoad() {
// Новая логика ДО оригинального метода
print("ViewDidLoad called for: (type(of: self))")
// Вызов ОРИГИНАЛЬНОЙ реализации (которая теперь находится под селектором swizzled_viewDidLoad)
self.swizzled_viewDidLoad()
// Новая логика ПОСЛЕ оригинального метода
}
}
// Вызов в AppDelegate:
UIViewController.swizzleViewDidLoad()
Важные предостережения:
- Swizzling нарушает инкапсуляцию и усложняет отладку.
- Выполняйте его только один раз (например, в
+loadили вinitialize). - Избегайте swizzling системных классов, если нет крайней необходимости.
- В Swift swizzling работает только для классов, наследующих от
NSObject, и методов, помеченных@objc dynamic.