紧跟着上篇文章(runtime中的交换方法method_exchangeImplementations存在的问题)
既然直接使用method_exchangeImplementations有问题,那么该怎么解决这个问题呢?我们可以同时使用class_replaceMethod来弥补method_exchangeImplementations存在的问题
看代码:
+ (void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method method1 = class_getInstanceMethod(self, @selector(eat));
Method method2 = class_getInstanceMethod(self, @selector(anotherEat));
BOOL didAddMethod = class_addMethod(self, @selector(eat), method_getImplementation(method2), method_getTypeEncoding(method2));
if (didAddMethod) {
class_replaceMethod(self,
@selector(anotherEat),
method_getImplementation(method1),
method_getTypeEncoding(method1));
}else{
method_exchangeImplementations(method1, method2);
}
});
}
class_addMethod是runtime中的动态添加方法,如果能够添加成功说明本类并没有实现这个要添加的方法,如果不能添加成功说明本类实现了这个方法。实现方法包括新增的方法和重写父类方法。
如果重写了父类的方法那么只是将Son中的eat方法和anotherEat的具体实现进行了替换并不会替换父类的eat方法的
具体实现,那么直接使用method_exchangeImplementations是完全没有问题的。
我们来分析下class_replaceMethod方法
class_replaceMethod
class_replaceMethod(self,
@selector(anotherEat),
method_getImplementation(method1),
method_getTypeEncoding(method1));
这块代码是将本类Son的anotherEat具体实现替换成了eat的具体实现,而并没有改变Father类中eat方法的具体实现。且已经在本类Son中动态添加了方法名为eat的方法,eat方法的具体实现是方法名为anotherEat的具体实现。
看打印:
runtime的两个方法替换[19134:1529903] self:<Son: 0x282b69240>
runtime的两个方法替换[19134:1529903] 爸爸吃东西...
runtime的两个方法替换[19134:1529903] 替换之后的吃的方法...
runtime的两个方法替换[19134:1529903] 爸爸吃东西...
使用了class_addMethod和class_replaceMethod能保证子类的分类替换方法并不会替换掉父类的方法的具体实现。
如果我们确保本类中要被替换掉方法不在父类中或者子类重写了要被替换掉的方法,那么我们可以直接使用method_exchangeImplementations。
方法替换的应用
方法替换在开发中的应用主要是用来拦截系统的方法,如防止数组、字典越界造成的崩溃,看这篇文章:
iOS开发中防止数组越界导致的崩溃(升级版)
为什么要使用dispatch_onece呢?
load方法虽然在didFinishLaunch方法之前只被调用了一次,但为了防止再手动调用load方法让两个方法再替换回去,所以就使用了dispatch_onece
简单一句话:
只替换本类中的的某个方法的具体实现,不去替换父类中某个方法的具体实现,避免可能导致的崩溃。
可以参考一个小demo:
runtime替换两个方法