如何在Swift中swizzle init [英] How to swizzle init in Swift

查看:190
本文介绍了如何在Swift中swizzle init的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我关注了 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

如果didAddMethod {
class_replaceMethod(self,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod,swizzledMethod);
}
}


解决方案

创建一个方法的选择器,你应该基于Obj C方法签名,因为使用Obj C运行时完成swizzling。



所以原来的选择器应该是

initWithIdentifier:source:destination:



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

ftg_initWithIdentifier:source:destination:



a href =https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html =nofollow>文档我可以找到它谈论从Obj C到Swift的翻译,但它看起来像是从Swift到Obj C的反向。



接下来, init ... 是一个实例方法,因此您需要进行两个更改。您需要将 class_getClassMethod 更改为 class_getInstanceMethod ,您需要删除 class

因此,当所有的内容都完成时,你的代码应该看起来像这样(为我工作)

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

如果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())
}


I'm following Swift & the Objective-C Runtime, it works for normal methods.

I like to swizzle init method, from my understanding, init is like a class method. So I tried swizzling init as instance and class method. But it does not seem to work

I can make it work using Objective C, just wonder how to make it work in Swift

Excerpted from my 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);
            }
        }

解决方案

When creating the selector for a method, you should base it off the Obj C method signature since swizzling is done using the Obj C runtime.

So the original selector should be

initWithIdentifier:source:destination:

Now this gets a little weird, since your init method is defined so that the label on the first argument is required (by having identifier twice), the selector you want to use is actually

ftg_initWithIdentifier:source:destination:

The only documentation I could find about it talk about the translation from Obj C to Swift but it looks like the reverse is happening from Swift to Obj C.

Next, init... is an instance method so you'll need to make two changes. You need to change class_getClassMethod to class_getInstanceMethod and you need to remove class from your ft_init... method.

So when all is said and done, your code should look like this (which worked for me)

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中swizzle init的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆