19.MethodSwizzing方法交换的坑
前言
MethodSwizzing 方法交换是比较常用的所谓 黑魔法。但正如武侠小说中的绝世武功一般,也存在使用不恰当发生 伤敌一千,自损八百 的情况。
本文就带你来探索一下其中的坑,避免走火入魔。
一、 MethodSwizzingTool
为了方便,我们封装一下常规的方法交换逻辑
@implementation MethodSwizzingTool
+ (void)swizzingClass:(Class)cls oldSEL:(SEL)oldSel toNewSel:(SEL)newSel {
if (!cls) { return; }
Method oldM = class_getInstanceMethod(cls, oldSel);
Method newM = class_getInstanceMethod(cls, newSel);
method_exchangeImplementations(oldM, newM);
}
@end1.1 验证是否有效
调用:
输出:
嗯~感觉封装的没问题。
二、 子类的坑
2.1 用自类方法替换父类方法,会怎样?
思考:
在子类
RYSubModel中用子类的subFunctionA替换父类的functionA,子类实例和父类实例分别调用functionA会是什么样的结果呢?(父类中未做交换)
子类代码如下:
调用:
输出:
分析
用子类中的方法 subFunctionA 替换父类中的方法 functionA, functionA 的实现变成了 subFunctionA。
2.2 调用原实现
一般我们交换方法后想要继续调用原本的实现一般会如上文中functionB那样调用一下自己。
那么我们在用子类中的方法 subFunctionA 替换了父类中的方法 functionA 后想要继续调用 functionA 同理应该这么写:
调用:
输出:

为什么呢?居然找不到了
分析
我们在用子类中的方法 subFunctionA 替换了父类中的方法 functionA 后
父类中:
functionA 调用 subFunctionA
但是父类本身方法列表中并没有 subFunctionA ,所以父类就报了 unrecognized selector 的错误。
修改调用
如果我们只调用子类:
输出:
这里就是正常的
三、 优化 MethodSwizzingTool
我们能不能优化 MethodSwizzingTool 来防止这样的问题出现呢?
可以!
3.1 优化思路
出现上面找不到方法的原因是:子类用自己的实现直接替换了父类的方法
那么我们能不能为子类动态添加一个和父类一样的方法呢?子类中进行替换的时候就不会影响父类了
3.2 编写优化代码
3.3 优化代码流程分析
本案例子类调用流程分析:
调用方法,准备用子类中的方法
subFunctionA替换的方法functionAMethod oldM是从父类获取到的方法(SEL: functionA, IMP: functionA)Method newM是从子类自己获取到的方法(SEL: subFunctionA, IMP: subFunctionA)是否可以成功添加方法:
(SEL: functionA, IMP: subFunctionA)否:已存在
SEL: functionA的方法是:不存在
SEL: functionA的方法将子类的
(SEL: subFunctionA, IMP: subFunctionA)替换为(SEL: subFunctionA, IMP: functionA)

父类实例
[ry functionA]调用没有受子类方法交换的影响
子类实例
[sub functionA]没有出现父类调用找不到方法的情况
四、 父类的 functionA 也没有实现呢?

4.1 分析

由于并不存在 functionA 的实现,所以这里的替换方法并没有成功。 subFunctionA 的调用就直接递归死循环了。
4.2 再优化
通过设置默认实现的方式来避免死循环,新实现也为空的情景可以类似处理,这里就不赘述了。

总结
黑魔法虽好,使用也得倍加小心!
尤其要对方法的本质以及方法调用的流程烂熟于心才能随心所欲,内功到家,进阶武功才不会伤及自身。
Last updated
Was this helpful?