首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >方法Swizzling无效。

方法Swizzling无效。
EN

Stack Overflow用户
提问于 2015-10-13 07:42:11
回答 1查看 4K关注 0票数 8

我想使用这种方法,但是我连简单的例子都找不到。我可能误解了这个概念,但据我所知,它允许交换方法实现。

给定两个方法,A和B,我想交换它们的实现,这样调用A就会执行B。我看到了一些swizzling的例子(example1example2)。我创建了一个带有类的新项目来测试这个项目。

代码语言:javascript
复制
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");
    }
}

然后我试着用

代码语言:javascript
复制
var s = Swizzle();
s.method();

预期输出为"B",但仍在打印"A“。从我的代码中可以看到,我在执行swizzle操作之前和之后都包含了每个IMP的打印。这些打印显示,交换确实发生了,但输出保持不变。

输出:

代码语言:javascript
复制
0x000000010251a920
0x000000010251ad40
0x000000010251ad40
0x000000010251a920
A

在让这些改变生效的时候,我有什么遗漏吗?

PS。目前使用XCode 7.0.1

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-10-13 08:23:18

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

代码语言:javascript
复制
class Swizzle: NSObject
{
    dynamic func method()
    {
        print("A")
    }
}

修改声明,它应该能工作。

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

  • 您的类必须扩展NSObject
  • 要使用swizzle的函数必须具有dynamic属性。

有关为什么需要这样做的完整解释,请参阅使用Swift与Cocoa和Objective

需要动态调度 虽然@objc属性将Swift公开给Objective运行时,但它并不保证动态分配属性、方法、下标或初始化器。Swift编译器仍然可以取消虚拟化或内联成员访问,以优化代码的性能,从而绕过Objective运行时。使用dynamic修饰符标记成员声明时,总是动态地分配对该成员的访问。因为使用dynamic修饰符标记的声明是使用Objective运行时分派的,所以它们被隐式标记为@objc属性。 很少需要动态调度。但是,当您知道在运行时dynamic 的实现被替换时,必须使用修饰符。例如,您可以在Objective运行时使用method_exchangeImplementations函数来在应用程序运行时交换方法的实现。如果Swift编译器将方法的实现内联或对其进行去虚拟化访问,则不会使用新的实现。

Swift 3更新:

在GCD方面已经有了一些变化,dispatch_once不再可用了。要执行相同的一次操作,我们可以将代码封装在全局静态类常量的初始化块中。

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

代码语言:javascript
复制
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())

SWIFT2.2更新:

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

代码语言:javascript
复制
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

票数 17
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33096873

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档