Swift中使用Method Swizzling,对比OC中,需要满足两个条件

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看132 回复回复1 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 132|回复: 1
收起左侧

Swift中使用Method Swizzling,对比OC中,需要满足两个条件

[复制链接]
swifter 发表于 2016-8-10 01:07:55 | 显示全部楼层 |阅读模式
快来登录
获取最新的苹果动态资讯
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码

什么?还没听说过Method Swizzling?

一种内部钩子啦,hook~

Swift中使用Method Swizzling,对比OC中,需要满足两个条件

Swift中使用Method Swizzling,对比OC中,需要满足两个条件 - 敏捷大拇指 - Swift中使用Method Swizzling,对比OC中,需要满足两个条件





OC中的 Method Swizzling

iOS中的黑魔法 Method Swizzlin g经常会用到,在OC中的使用方式是重写类的+ (void)load方法,运行时交换方法。

[Objective-C] 纯文本查看 复制代码
+ (void)_load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SwizzleMethod([self class],
                        @selector(originalMethod),
                        @selector(trans_originalMethod));
       });
}


void SwizzleMethod(Class c, SEL origSEL, SEL newSEL) {
    Method origMethod = class_getInstanceMethod(c, origSEL);
    Method newMethod = nil;
    if (!origMethod) {
        origMethod = class_getClassMethod(c, origSEL);
        if (!origMethod) {
                return;
        }
        newMethod = class_getClassMethod(c, newSEL);
        if (!newMethod) {
            return;
        }
    }else{
        newMethod = class_getInstanceMethod(c, newSEL);
        if (!newMethod) {
            return;
        }
    }

    // 自身已经有了就添加不成功,直接交换即可
    if(class_addMethod(c, origSEL, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))){
        class_replaceMethod(c, newSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    }else{
        method_exchangeImplementations(origMethod, newMethod);
    }
}


其实在load方法中,可以不用使用GCD的dispatch_once方法,因为load本身就是只执行一次(方法交换需要且只能执行一次)。




2、Swift中的 Method Swizzling

由于Swift中没有load方法,所以只能写在initialize中,这是调用类中第一个方法前的地方。

[Swift] 纯文本查看 复制代码
extension UIScrollView {
    public override static func initialize() {
        struct Static {
            static var token: dispatch_once_t = 0
        }

        // Make sure not subclass
        if self !== UIScrollView.self {
            return
        }

        dispatch_once(&Static.token) {
            let originalSelector = NSSelectorFromString("handlePan:")
            let swizzledSelector = NSSelectorFromString("pm_handlePan:")

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

    // MARK: - Method Swizzling
    func pm_handlePan(pan: UIPanGestureRecognizer) {
        pm_handlePan(pan)

        if delegate != nil && delegate!.respondsToSelector(NSSelectorFromString("scrollViewDidPan:")) {
            delegate?.performSelector(NSSelectorFromString("scrollViewDidPan:"), withObject: pan)
        }
    }
}


不过,Swift使用Method Swizzling需要满足两个条件:

  • 包含 swizzle 方法的类需要继承自 NSObject
  • 需要 swizzle 的方法必须有动态属性(dynamic attribute)


通常添加下,我们都是交换系统类的方法,一般不需要关注着两点。但是如果我们自定义的类,也需要交换方法就要满足这两点了(这种状况是极少的,也是应该避免的)

更多详情请查看苹果文档

那么,自定义的类就得满足动态派发的特性:

当@objc属性将你的Swift API暴露给Objective-C runtime时,不能确保属性、方法、初始器的动态派发。Swift编译器可能为了优化你的代码,而绕过Objective-C runtime。当你指定一个成员变量为dynamic时,访问该变量就总会动态派发了。因为在变量定义时指定dynamic后,会使用Objective-C runtime来派发。

所以,需要对想要替换的方法声明为dynamic。如下:

[Swift] 纯文本查看 复制代码
class MyOwnClass : NSObject {
    dynamic func methodOriginal()->Int{
        return 1
    }
}


extension MyOwnClass {

    //在 Objective-C 中,我们在 load() 方法进行 swizzling。但Swift不允许使用这个方法。
       override class func initialize() {
        struct Static {
            static var token: dispatch_once_t = 0;
        }

        dispatch_once(&Static.token) {
            let originalSelector = Selector("methodOriginal");
            let swizzledSelector = Selector("methodSwizzled");

            let originalMethod = class_getInstanceMethod(self, originalSelector);
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector);

            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    }

    func methodSwizzled()->Int{
        // swizzling 后, 该方法就不会递归调用
        return methodSwizzled()+1
    }
}

var c = MyOwnClass()
print(methodOriginal())  //2
print(methodSwizzled())  //1


iOS中的黑魔法 Method Swizzling 不能随便用,只有在很了解并且比较特殊的情况才能使用,否则会有副作用的。




作者:Roylee

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

评分

参与人数 1金钱 +10 贡献 +10 专家分 +10 收起 理由
Anewczs + 10 + 10 + 10 32个赞!专家给力!

查看全部评分

i0n1c 发表于 2016-8-10 16:16:05 | 显示全部楼层
玩得溜~ 黑客思维
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励
热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表