Method Swizzing备注

201 阅读1分钟

大部分内容参考(复制)自 此文章

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method oriMethod = class_getInstanceMethod(self, @selector(doInstance));
        Method swiMethod = class_getInstanceMethod(self, @selector(fx_doInstance));
        /// 子类如果实现了父类中要替换的方法, 通过`class_addMethod`是会失败并返回false的.
        BOOL didAddMethod = class_addMethod(self, @selector(doInstance), method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
        if (didAddMethod) {
        /// 添加成功表示当前子类并没有实现父类的'doInstance'方法, 添加完之后'doInstance' SEL
        /// 指向swiMethod的IMP, 此时调用'doInstance'的效果和调用'fx_doInstance'效果一样, 
        /// 因此需要把fx_doInstance的方法实现替换成oriMethod IMP, 这样才能完成替换
            class_replaceMethod(self, @selector(fx_doInstance), method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
        } else {
        /// 添加失败表示当前子类已经重写了父类的'doInstance'方法, 此时, 可以直接替换两个方法的实现.
            method_exchangeImplementations(oriMethod, swiMethod);
        }
    });
}
  • 以下是原文的解释, 结合注释来看应该会更容易理解.

  • 通过class_addMethod给Class添加方法(class_addMethod不会取代本类中已存在的实现,只会覆盖本类中继承父类的方法实现

    • 取得新方法swiMethod的实现和方法类型
    • 往方法名@selector(fx_doInstance)添加方法
    • class_addMethod 把新方法实现放到旧方法名中,此刻调用doInstance就是调用fx_doInstance,但是调用fx_doInstance会崩溃
  • 根据didAddMethod判断是否添加成功

    • 添加成功说明之前本类没有实现——class_replaceMethod替换方法
    • 添加失败说明之前本类已有实现——method_exchangeImplementations交换方法
    • class_replaceMethoddoInstance方法实现替换掉fx_doInstance中的方法实现