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)
    }
}
Другие вопросы по тегам