iOS Method Swizzling

标准的写法


+ (void)load {
    Method oldM = class_getInstanceMethod([self class], @selector(test));
    Method newM = class_getInstanceMethod([self class], @selector(test_new));
    
    IMP oldIMP = method_getImplementation(oldM);
    IMP newIMP = method_getImplementation(newM);
    
    BOOL isAddSucceed = class_addMethod([self class], @selector(test), newIMP, method_getTypeEncoding(newM));
    if (isAddSucceed) {
        class_replaceMethod([self class], @selector(test_new), oldIMP, method_getTypeEncoding(oldM));
    }else {
        method_exchangeImplementations(oldM, newM);
    }
}

好多同学很奇怪为啥要加个 class_addMethod方法

1.打个比方 父类 Person 子类Son
条件:
如果 test方法 只在父类Person只实现,如果子类不实现test
不调用 class_addMethod 方法。
影响:
[son test]; 调用OK 一点问题都没有
但是如果是 [person test]; 就会调用到 son的实现的new_test;
显示不符合逻辑了 这样会把父类Person搞的很坑。
2.防止父类 Person 跟子类 Son 都没有实现test方法 那直接method_exchangeImplementations 实现后 会崩溃

其实更加严禁的方法应该这么写


+ (void)load {
    Method oldM = class_getInstanceMethod([self class], @selector(test));
    Method newM = class_getInstanceMethod([self class], @selector(test_new));
    
    IMP oldIMP = method_getImplementation(oldM);
    IMP newIMP = method_getImplementation(newM);
    
    BOOL isAddSucceed = class_addMethod([self class], @selector(test), newIMP, method_getTypeEncoding(newM));
    if (isAddSucceed) {
        // 如果 class_addMethod 成功了,说明之前 fromClass 里并不存在 originSelector,所以要用一个空的方法代替它,以避免 class_replaceMethod 后,后续 toClass 的这个方法被调用时可能会 crash
        oldIMP = oldIMP ?: imp_implementationWithBlock(^(id selfObject) {});
         const char *oldTypeEncoding = method_getTypeEncoding(oldM) ?: "v@:";
        class_replaceMethod([self class], @selector(test_new), oldIMP, oldTypeEncoding);
    }else {
         method_exchangeImplementations(oldM, newM);
    }

}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容