UIView метод Swizzling Swift 3
Я пытаюсь реализовать метод Swizzling в Swift 3 на основе ответа Как реализовать метод Swizzling Swift 3.0?
Вот мой код:
// MARK: - Swizzling
private let swizzling: (UIView.Type) -> () = { view in
let originalSelector = #selector(view.awakeFromNib)
let swizzledSelector = #selector(view.swizzled_localization_awakeFromNib)
let originalMethod = class_getInstanceMethod(view, originalSelector)
let swizzledMethod = class_getInstanceMethod(view, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
extension UIView {
open override class func initialize() {
guard self === UIView.self else {
return
}
swizzling(self)
}
func swizzled_localization_awakeFromNib() {
swizzled_localization_awakeFromNib()
if let localizableView = self as? Localizable {
localizableView.localize()
}
}
}
Но при запуске приложения происходит сбой по причине:
'- [UINavigationController swizzled_localization_awakeFromNib]: нераспознанный селектор отправлен на экземпляр 0x7fc7c8820400'
Я не могу понять, почему swizzled_localization_awakeFromNib вызвал UINavigationController. Я использую это в проекте obj-c, это может быть причиной? Это работало хорошо в быстром 2 через dispatch_once.
Я пробовал установить точку останова перед тем, как начать swizzling(самостоятельно), и однажды он вызвал UIView, как и ожидалось.
1 ответ
Проблема в том, что awakeFromNib
это метод NSObject
а не из UIView
, Ваш код свистит NSObject
метод с методом UIView
и вызов исходного метода завершается сбоем при вызове метода swizzled UINavigationController
(или любой другой подкласс NSObject
который не является подклассом UIView
).
Решение состоит в том, чтобы сначала попытаться добавить метод swizzled с оригинальным именем (как описано в http://nshipster.com/method-swizzling/):
private let swizzling: (UIView.Type) -> () = { view in
let originalSelector = #selector(view.awakeFromNib)
let swizzledSelector = #selector(view.swizzled_localization_awakeFromNib)
let originalMethod = class_getInstanceMethod(view, originalSelector)
let swizzledMethod = class_getInstanceMethod(view, swizzledSelector)
let didAddMethod = class_addMethod(view, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(view, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}