如何在Swift中swizzle init

时间:2015-12-16 16:57:17

标签: ios swift class init swizzle

我跟随Swift & the Objective-C Runtime,它适用于普通方法。

我喜欢swizzle init方法,从我的理解来看,init就像一个类方法。所以我尝试将swizzling init作为实例和类方法。但它似乎不起作用

我可以使用Objective C使其工作,只是想知道如何使它在Swift中工作

摘自我的gist

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

2 个答案:

答案 0 :(得分:3)

在为方法创建选择器时,您应该将其基于Obj C方法签名,因为使用Obj C运行时完成了swizzling。

所以原来的选择器应该是

initWithIdentifier:source:destination:

现在这有点奇怪了,因为定义了init方法以便第一个参数上的标签是必需的(通过identifier两次),你想要使用的选择器实际上是

ftg_initWithIdentifier:source:destination:

我能找到的唯一documentation是关于从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())
}

答案 1 :(得分:1)

这是在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);
    }
}