【问题标题】:How to swizzle init in Swift如何在 Swift 中调配 init
【发布时间】:2016-03-22 22:16:54
【问题描述】:

我正在关注Swift & the Objective-C Runtime,它适用于普通方法。

我喜欢swizzle init 方法,在我的理解中,init 就像一个类方法。所以我尝试将 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);
            }
        }

【问题讨论】:

  • 你的类继承自什么?我相信 swizzling 只适用于继承自 NSObject 或使用 @objc 定义为目标 c 类的类。
  • @CleverError 我在问题中链接了我的要点,你能看看

标签: ios swift class init swizzle


【解决方案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);
    }
}

【讨论】:

  • 我投了这个票,因为直到你的帖子我才知道#selector(UIImage.init(named:))。谢谢!一件事:Static methods may only be declared on a type 是我在全球范围内得到的。
  • 另外,如果这些方法应该存在于 uiimage 扩展中,我无法按照这些方法的要求将 self 转换为 AnyClass...
【解决方案2】:

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

所以原来的选择器应该是

initWithIdentifier:source:destination:

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

ftg_initWithIdentifier:source:destination:

我能找到的唯一一个 documentation 谈论从 Obj C 到 Swift 的翻译,但看起来从 Swift 到 Obj C 的情况正好相反。

接下来,init... 是一个实例方法,因此您需要进行两项更改。您需要将class_getClassMethod 更改为class_getInstanceMethod,并且需要从ft_init... 方法中删除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))

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

【讨论】:

    猜你喜欢
    • 2014-08-09
    • 2015-10-20
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多