Method-Swizzling 方法交换

method-swizzling 是什么?

method-swizzling的含义是方法交换,其主要作用是在运行时将一个方法的实现替换成另一个方法的实现,这就是我们常说的iOS黑魔法,

在OC中就是利用method-swizzling实现AOP,其中AOP(Aspect Oriented Programming,面向切面编程)是一种编程的思想,区别于OOP(面向对象编程)

OOP和AOP都是一种编程的思想ios_lowLevel

OOP编程思想更加倾向于对业务模块的封装,划分出更加清晰的逻辑单元;

而AOP是面向切面进行提取封装,提取各个模块中的公共部分,提高模块的复用率,降低业务之间的耦合性。

每个类都维护着一个方法列表,即methodList,methodList中有不同的方法即Method,每个方法中包含了方法的sel和IMP,方法交换就是将sel和imp原本的对应断开,并将sel和新的IMP生成对应关系


method-swizzling涉及的相关API

通过sel获取方法Method

class_getInstanceMethod:获取实例方法

class_getClassMethod:获取类方法

method_getImplementation:获取一个方法的实现

method_setImplementation:设置一个方法的实现

method_getTypeEncoding:获取方法实现的编码类型

class_addMethod:添加方法实现

class_replaceMethod:用一个方法的实现,替换另一个方法的实现,即aIMP 指向 bIMP,但是bIMP不一定指向aIMP

method_exchangeImplementations:交换两个方法的实现,即 aIMP -> bIMP, bIMP -> aIMP

坑点1:method-swizzling使用过程中的一次性问题

所谓的一次性就是:mehod-swizzling写在load方法中,而load方法会主动调用多次,这样会导致方法的重复交换,使方法sel的指向又恢复成原来的imp的问题

解决方案

可以通过单例设计原则,使方法交换只执行一次,在OC中可以通过dispatch_once实现单例

坑点2:子类没有实现,父类实现了

在下面这段代码中,LGPerson中实现了personInstanceMethod,而LGStudent继承自LGPerson,没有实现personInstanceMethod,运行下面这段代码会出现什么问题?



下面是封装好的method-swizzling方法


通过实际代码的调试,s调用personInstanceMethod 正常 而在p调用personInstanceMethod方法时崩溃,

[s personInstanceMethod];中不报错是因为student中的imp交换成了lg_studentInstanceMethod,而LGStudent中有这个方法(在LG分类中),所以不会报错

崩溃的点在于[p personInstanceMethod];,其本质原因:LGStudent的分类LG中进行了方法交换,将person中imp交换成了LGStudent中的lg_studentInstanceMethod,然后需要去LGPerson中的找lg_studentInstanceMethod,但是LGPerson中没有lg_studentInstanceMethod方法,即相关的imp找不到,所以就崩溃了

优化:避免imp找不到

通过class_addMethod尝试添加你要交换的方法

如果添加成功,即类中没有这个方法,则通过class_replaceMethod进行替换,其内部会调用class_addMethod进行添加

如果添加不成功,即类中有这个方法,则通过method_exchangeImplementations进行交换


下面是class_replaceMethod、class_addMethod和method_exchangeImplementations的源码实现


其中class_replaceMethod和class_addMethod中都调用了addMethod方法,区别在于bool值的判断,下面是addMethod的源码实现

坑点3:子类没有实现,父类也没有实现,下面的调用有什么问题?


经过调试,发现运行代码会崩溃

原因是栈溢出,递归死循环了,那么为什么会发生递归呢?----主要是因为personInstanceMethod没有实现,然后在方法交换时,始终都找不到oriMethod,然后交换了寂寞,即交换失败,当我们调用personInstanceMethod(oriMethod)时,也就是oriMethod会进入LG中lg_studentInstanceMethod方法,然后这个方法中又调用了lg_studentInstanceMethod,此时的lg_studentInstanceMethod并没有指向oriMethod,然后导致了自己调自己,即递归死循环

优化:避免递归死循环

如果oriMethod为空,为了避免方法交换没有意义,而被废弃,需要做一些事情

通过class_addMethod给oriSEL添加swiMethod方法

通过method_setImplementation将swiMethod的IMP指向不做任何事的空实现


你可能感兴趣的:(Method-Swizzling 方法交换)