Как завладеть init в Swift

Я следую за Swift и Objective-C Runtime, он работает для обычных методов.

Мне нравится swizzle метод init, насколько я понимаю, init похож на метод класса. Так что я попробовал swizzling init как метод экземпляра и класса. Но это не похоже на работу

Я могу заставить его работать с помощью Objective C, просто интересно, как заставить это работать в Swift

Выдержка из моей сути

dispatch_once(&Static.token) {
            let originalSelector = Selector("init:source:destination:")
            let swizzledSelector = Selector("ftg_init:source:destination:")

            let originalMethod = class_getClassMethod(self, originalSelector)
            let swizzledMethod = class_getClassMethod(self, swizzledSelector)

            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);
            }
        }

1 ответ

Решение

При создании селектора для метода вы должны основывать его на сигнатуре метода Obj C, так как Swizzling выполняется с использованием среды выполнения Obj C.

Таким образом, оригинальный селектор должен быть

initWithIdentifier:source:destination:

Теперь это становится немного странным, так как ваш метод init определен так, что необходима метка в первом аргументе (имея identifier дважды), селектор, который вы хотите использовать, на самом деле

ftg_initWithIdentifier:source:destination:

Единственная документация, которую я мог найти об этом, говорит о переводе с Obj C на Swift, но похоже, что обратное происходит со Swift на Obj C.

Следующий, init... это метод экземпляра, поэтому вам нужно сделать два изменения. Вам нужно изменить class_getClassMethod в class_getInstanceMethod и вам нужно удалить class от твоего ft_init... метод.

Поэтому, когда все сказано и сделано, ваш код должен выглядеть следующим образом (что сработало для меня)

dispatch_once(&Static.token) {
    let originalSelector = Selector("initWithIdentifier:source:destination:")
    let swizzledSelector = Selector("ftg_initWithIdentifier:source:destination:")

    let originalMethod = class_getInstanceMethod(self, originalSelector)
    let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)

    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);
    }
}

func ftg_init(identifier identifier: String!,
    source: UIViewController,
    destination: UIViewController) -> UIStoryboardSegue {

    return ftg_init(identifier: identifier,
        source: source,
        destination: destination.ftg_resolve())
}

Вот способ сделать это в Swift 4.2 для инициализатора UIImage:

@objc static func ftg_imageNamed(_ name: String) -> UIImage? {
    ...
}

private static func swizzleInitImplementation() {
    let originalSelector = #selector(UIImage.init(named:))
    let swizzledSelector = #selector(UIImage.ftg_imageNamed(_:))

    guard let originalMethod = class_getClassMethod(self, originalSelector), let swizzledMethod = class_getClassMethod(self, swizzledSelector) else {
        assertionFailure("The methods are not found!")
        return
    }

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