1. method-swizzling 是什么?
Method-Swizzling实际就是更换方法所对应的实现函数,其主要作用是在运行时将一个方法的实现替换成另一个方法的实现,这就是我们常说的 iOS黑魔法。Method-Swizzling比较常用的应用是防止数组、字典越界等。
下面代码是否会递归调用呢?答案是不会的,因为方法交换后,yj_studentInstanceMethod
实际上是通过[self personInstanceMethod]
调用的,而[self yj_studentInstanceMethod]
则是调用 personInstanceMethod
,所以不会递归调用
2. method-swizzling 坑点
2.1 method-swizzling使用过程中确保只执行一次
method-swizzling 多次调用会导致方法的重复交换,无法保证是否达到了最终的交换效果。如何解决这个问题呢?
解决方案:
在 +load
方法中执行,且使用 dispatch_once
包裹;这样就可以保证方法交换只执行一次
2.2 子类没有实现,父类实现了
如果交换的方法在子类中没有实现(继承自父类的方法),父类里有实现,那么交换的时候会不会报错呢?
运行后发现报错了。这是因为personInstanceMethod
已经交换成了 yj_studentInstanceMethod
。而在 yj_studentInstanceMethod
里面调用了 [self yj_studentInstanceMethod]
,而 YJPerson
里面没有 yj_studentInstanceMethod
方法,所以报错。
解决方案:
先通过 class_addMethod
尝试给自己添加要交换的方法,如果添加成功,即表明类中没有这个方法,则通过 class_replaceMethod
进行替换,如果添加失败则说明类中有这个方法,正常进行 method_exchangeImplementations
。
2.3 父类子类都没有实现。
如果父类子类都没有实现,会有什么问题呢?运行后发现会递归调用。那么说明没有交换成功。所以 yj_studentInstanceMethod
还是指向自己的imp
。当调用 personInstanceMethod
之后,会进入 yj_studentInstanceMethod
,之后 [self yj_studentInstanceMethod]
就会无限递归调用自己,最终导致栈溢出
解决方案:
判断 oriMethod
是否为空,如果为空则 添加swiMethod
方法并且 添加imp实现