Swift - 方法交换

9
这是用Objective-C编写的方法交换代码。我很难将其转换为Swift。
void MPApplicationDidRegisterForRemoteNotificationsWithDeviceToken(id self, SEL _cmd, UIApplication *application, NSData *deviceToken) {
    [[MPPush shared] appRegisteredForRemoteNotificationsWithDeviceToken:deviceToken];

    IMP original = [MPAppDelegateProxy originalImplementation:_cmd class:[self class]];
    if (original)
        ((void(*)(id, SEL, UIApplication *, NSData*))original)(self, _cmd, application, deviceToken);
}  
< p > Swiftify 无法正确转换上述代码。

我尝试过这样做,但不确定如何传递参数并在 Swift swizzling 方法中使用上述参数。以下是我尝试将其转换为 Swift 的失败尝试(代码甚至无法编译):

var MPApplicationDidRegisterForRemoteNotificationsWithDeviceToken: Void {
        //     TODO:   MPPush.shared.app

        let original = MPAppDelegateProxy.proxyAppDelegate.originalImplementation(selector: cmd, forClass: type(of: self))
    }(self: Any, _cmd: Selector, application: UIApplication, deviceToken: Data)

@Cristik:我不能用Swift做这个吗? - Nitish
这个方法是在哪个类中定义的? - ielyamani
@Cristik:因为它不是一个函数。Objective-C的函数以-开头。 - Nitish
@Cristik:那么,这应该只是一个简单的Swift函数吗? - Nitish
1个回答

7

扩展您的类:

extension YourClassName {
    static let classInit: () -> () = {
        let originalSelector = #selector(originalFunction)
        let swizzledSelector = #selector(swizzledFunction)
        swizzle(YourClassName.self, originalSelector, swizzledSelector)
    }
    
    @objc func swizzledFunction() {
        //Your new implementation
    }
}

您的类(YourClassName)应该继承自NSObject,并且originalSelector应该是一个dynamic方法。

swizzle是一个闭包,用于交换实现:

private let swizzle: (AnyClass, Selector, Selector) -> () = { fromClass, originalSelector, swizzledSelector in
    guard 
        let originalMethod = class_getInstanceMethod(fromClass, originalSelector),
        let swizzledMethod = class_getInstanceMethod(fromClass, swizzledSelector)
        else { return }
    method_exchangeImplementations(originalMethod, swizzledMethod)
}

你可以在需要进行swizzling的类中定义swizzle。例如,在AppDelegate类定义中,然后在AppDelegate.init()中进行swizzling:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    override init() {
        super.init()
        YourClassName.classInit()
    }
}

例子

以下是 swizzling 的一个具体例子,它 交换了 两个只有一个参数的方法的实现:

class Example: NSObject {
    @objc dynamic func sayHi(to name: String) {
        print("Hi", name)
    }
}

extension Example {
    public class func swizzleMethod() {
        guard
            let originalMethod = class_getInstanceMethod(Example.self, #selector(Example.sayHi(to:))),
            let swizzledMethod = class_getInstanceMethod(Example.self, #selector(Example.sayHello(to:)))
            else { return }
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
    @objc func sayHello(to name: String) {
        print("Hello", name)
    }
}

Example.swizzleMethod()

let a = Example()
a.sayHi(to: "Nitish")        //Hello Nitish

Swift本地方法交换

从Swift 5.1开始,有一种本地版本的方法交换,不依赖于Objective-C的消息传递。可以在替换函数上使用@_dynamicReplacement修饰符,并将要替换的函数的名称作为参数。被替换的函数必须带有@dynamic修饰符,除非使用了-enable-implicit-dynamic编译标志,这使得编译器假定每个合格实体都已标记了该修饰符。

例如:

dynamic func original() {
    print("I am the original")
}

@_dynamicReplacement(for: original)
func replacement() {
    print("I am the replacement")
}

original() // prints "I am the replacement"

欲了解关于动态方法替换的更多细节,请访问此Swift论坛页面


你能为我解释一下你的答案吗?我在转换和调用MPApplicationDidRegisterForRemoteNotificationsWithDeviceToken时遇到了困难。 - Nitish
我已经添加了一个具体的方法交换示例。 - ielyamani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接