动态添加方法的代码分析
之前自己写代码时只是单纯的使用method_exchangeImplementations
来进行方法的相互替换,但是偶然间看到别人写的方法替换,发现很不错,很严谨, 代码如下:
+ (void)load{ static dispatch_once_t token; dispatch_once(&token, ^{ Class class = [self class]; SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(skx_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (success) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } });}
初看方法,第一感觉比较困惑的是为什么在load方法中还要进行dispatch_once
. 对此的解释是:虽然load方法只执行一次, 但是说不准某些人会显示调用呢. 所以为了稳妥, 还是加个dispatch_once.
method_exchangeImplementations
这个方法就不介绍了,主要作用是交换两个方法的IMP.
主要看这里:
BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));if (success) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));}
首先进行的第一步是addMethod
. why? 而且还是拿的原始的selector去进行方法的添加. 但是我们所需要的不正是对原始selector对应的IMP的替换吗? 为什么会有success这个判断呢?
答案只有一个, 那就是此类中其实不存在此方法.这个方法是从父类继承而来的.这时就需要先判断一下:如果方法实际是从父类继承而来的, 那么addMethod
就会返回true, 进而再去进行方法的替换.
在此方法是从父类继承而来的情况下, 方法的method_exchangeImplementations
还是可以成功的,子类正常使用还是没问题的. 但是如果父类调用了这个被替换的方法, 那么就会崩溃. 因为方法的添加替换都是在子类中进行的, 父类找不到这个方法.
综上, 别人写的代码还是很严谨啊, 还要多学习.