oc中的方法hook(Method Swizzling)
2020-06-26 本文已影响0人
三国韩信
Method Swizzling 的坑
hook方法一般在load方法里执行,那么load方法的调用顺序也会影响到。load方法的顺序:父类->子类->子类的分类->父类的分类(不走方法的查找流程)
1、load方法要用dispatch_once包裹,保证只运行一次。单利设计
2、我们在交换系统方法的时候,有些类不能用类名直接去交换,它们是类簇的情况,比如NSArray、NSMutablArray等。它们要用"__NSArrayI"去交换方法。
3、hook子类没有实现的(父类实现了)的方法。
- 导致现象:这种情况下,会把父类的实现给交换了,以后父类的其他子类调用父类方法或者父类自己调改方法时会出现找不到方法的情况。
- 解决方案:在交换方法时,先尝试给自己添加一个同名的方法,如果添加失败,证明子类有重写过这个方法。如果添加成功,表面自己之前没有重写过,添加成功就可以避开这个坑了。然后在进行方法交换。
4、交换根本没有实现的方法。 顾名思义就是说交换的方法,不仅本类未实现,其父类中也没有实现,那么交换就没有成功,那么就会出现自己调用自己的情况,出现死循环。
+ (void)bestMethodSwizzlingWithClass:(Class)cls oriSEL:(SEL)oriSEL swizzledSEL:(SEL)swizzledSEL{
if (!cls) NSLog(@"传入的交换类不能为空");
Method oriMethod = class_getInstanceMethod(cls, oriSEL);
Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);
if (!oriMethod) {
class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
// IMP指向了一个空的block方法(空IMP)
method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){ }));
}
BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
if (didAddMethod) {
class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
}else{
method_exchangeImplementations(oriMethod, swiMethod);
}
5、当子类和父类都同时hook同一个方法的时候,那么情况就更复杂了
- 子类和父类都没实现,其实现就为基类的imp
- 子类实现了,父类没有实现
- 父类实现了,子类没有实现
6、父类的hook的swizzling selector方法和子类的swizzling selector同名的时候,会出现unrecognized selector 异常或死循环的情况。
总结: 对于项目中我们自己写method swizzling,最好就是子类和父类都实现了要hook的方法,哪怕是空实现也要,父类和子类的swizzling selector最好不要一样的命名。最好先Super类后Child类。RSSwizzle 是被很多人推荐框架,它用很复杂的方式解决了 What are the Dangers of Method Swizzling in Objective C? 中提到的一系列问题。不过引入它还是有一些成本的,只有在穷举的那些极端特殊情况下才使用它,文章开头处的交互方案代码已经能 Cover 到大部分情况了。
最后安利一篇博文,在这篇文章里杨萧玉大神就OC的Method Swizzling 的各种情况做了很详细的分析,相当的棒,膜拜大神!==> Objective-C-Method-Swizzling