方法搅拌不工作



我想利用方法搅拌,但我无法得到甚至简单的例子为我工作。我可能误解了这个概念,但据我所知,它允许交换方法实现。

给定两个方法A和B,我想交换它们的实现,这样调用A会执行B。我遇到了几个搅拌的例子(example1和example2)。我用一个类创建了一个新项目来测试这个

class Swizzle: NSObject
{
    func method()
    {
        print("A");
    }
}
extension Swizzle
{
    override class func initialize()
    {
        struct Static
        {
            static var token: dispatch_once_t = 0;
        }
        // make sure this isn't a subclass
        if (self !== Swizzle.self)
        {
            return;
        }
        dispatch_once(&Static.token)
        {
            let originalSelector = Selector("method");
            let swizzledSelector = Selector("methodExt");
            let originalMethod = class_getInstanceMethod(self, originalSelector);
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
            print(method_getImplementation(originalMethod));
            print(method_getImplementation(swizzledMethod));
            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);
            }
            print(method_getImplementation(originalMethod));
            print(method_getImplementation(swizzledMethod));
        }
    }
    func methodExt()
    {
        print("B");
    }
}
然后我尝试用 执行它
var s = Swizzle();
s.method();

预期输出是"B",但仍在打印"A"。正如您可以从我的代码中看到的,我已经包括了在swizzle操作之前和之后的每个IMP的打印。这些输出表明交换确实发生了,但输出保持不变。

输出:

0x000000010251a920
0x000000010251ad40
0x000000010251ad40
0x000000010251a920
A

在使这些更改生效时,我还缺少什么吗?

p。当前使用XCode 7.0.1

问题是您的method()缺少dynamic指令:

class Swizzle: NSObject
{
    dynamic func method()
    {
        print("A")
    }
}

修改声明,它应该可以工作。

在Swift中使用swizzling方法时,你的类/方法必须遵守两个要求:

  • 你的类必须扩展NSObject
  • 你想要混合的函数必须有dynamic属性

关于为什么需要这样做的完整解释,请查看Using Swift with Cocoa and Objective-C:

需要动态调度

@objc属性将你的Swift API暴露给Objective-C运行时,它不保证动态分派属性、方法、下标,或初始化式。 Swift编译器可能仍然会去虚拟化或内联成员访问来优化代码的性能,绕过Objective-C运行时。标记成员声明时使用dynamic修饰符,对该成员的访问总是动态的派遣。因为用dynamic修饰符标记的声明是使用Objective-C运行时分派,它们被隐式标记@objc属性

很少需要动态分派。但是,必须使用dynamic修饰符当您知道某个API的实现时在运行时被替换。例如,您可以使用method_exchangeImplementations函数在Objective-C运行时在应用程序运行时换出方法的实现。如果Swift编译器内联了or方法的实现去虚拟化访问它,新的实现将不会被使用

Swift 3 Update:

有一些关于GCD和dispatch_once不再可用的变化。要执行相同的一次性操作,可以将代码封装在全局静态类常量的初始化块中。

Swift语言保证这些代码在应用程序的生命周期内只执行一次。

class TestSwizzling : NSObject {
    dynamic func methodOne()->Int{
        return 1
    }
}
extension TestSwizzling {
    //In Objective-C you'd perform the swizzling in load(), 
    //but this method is not permitted in Swift
    override class func initialize()
    {
        struct Inner {
            static let i: () = {
                let originalSelector = #selector(TestSwizzling.methodOne)
                let swizzledSelector = #selector(TestSwizzling.methodTwo)                 
                let originalMethod = class_getInstanceMethod(TestSwizzling.self, originalSelector);
                let swizzledMethod = class_getInstanceMethod(TestSwizzling.self, swizzledSelector)                
                method_exchangeImplementations(originalMethod, swizzledMethod)
            }
        }
        let _ = Inner.i
    }
    func methodTwo()->Int{
        // It will not be a recursive call anymore after the swizzling
        return methodTwo()+1
    }
}
var c = TestSwizzling()
print(c.methodOne())
print(c.methodTwo())

Swift 2.2 Update:

我已经更新了新的#selector属性的原始示例:

class TestSwizzling : NSObject {
    dynamic func methodOne()->Int{
        return 1
    }
}
extension TestSwizzling {
    //In Objective-C you'd perform the swizzling in load(), 
    //but this method is not permitted in Swift
    override class func initialize()
    {
        struct Static
        {
            static var token: dispatch_once_t = 0
        }
        // Perform this one time only
        dispatch_once(&Static.token)
        {
                let originalSelector = #selector(TestSwizzling.methodOne)
                let swizzledSelector = #selector(TestSwizzling.methodTwo)                 
                let originalMethod = class_getInstanceMethod(self, originalSelector);
                let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)                
                method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
    func methodTwo()->Int{
        // It will not be a recursive call anymore after the swizzling
        return methodTwo()+1
    }
}
var c = TestSwizzling()
print(c.methodOne())
print(c.methodTwo())

如果你需要一个例子来玩,看看这个样例项目在github上

相关内容

  • 没有找到相关文章

最新更新