关于不同类的Method Swizzling
iOS的Method Swizzling是一个非常有意思的run time应用案例.用它可以实现AOP,也可以用来hook很多API,进行很多hack的操作.
相关资料很多,总体来讲就是调换两个Method的Imp,也就是调换函数指针.在很多AOP的案例中,使用这种方式hook住关键方法,在递归调用方法前/后实现AOP.例如:在一个UIViewController的category中,可以这样
+ (void)load {
Method currentDidLoadMethod = class_getInstanceMethod([UIViewController class], @selector(viewDidLoadAOP));
Method oriDidLoadMethod = class_getInstanceMethod([UIViewController class], @selector(viewDidLoad));
method_exchangeImplementations(currentDidLoadMethod, oriDidLoadMethod);
}
- (void)viewDidLoadAOP {
//在这里写代码,可以类似成为Before
[self viewDidLoadAOP];//这里递归调用自己
//在这里写代码,可以类似成为After
但是如果交换不同类之间的方法,这样调用就会crash.
比如有A,B两个类,分别有a方法和b方法.我们调用A类的a方法,希望通过method swizzling对A方法进行处理.
调用A类的a方法
A *aClass = [A new];
[aClass a];
a方法
- (void)a {
NSLog(@"a");
}
b方法(我们希望增强a方法,所以递归调用)
- (void)b {
[self b];//递归调用
NSLog(@"b");
}
这样就会导致crash,报错为A类中找不到b这个selector.
原因是当执行[aClass a]的时候,因为交换Imp的原因,则会进入到b方法中.而b中此时的self,是指的A,因为交换仅仅改变Imp,receiver和selector都不会改变,A类中没有b这个方法,当然会crash.
解决方案是:
如果需要在不同的类中交换方法,一定要注意此时的self指的是什么.如果需要调用self没有的方法,那么使用class_addMethod将不存在的方法add进去即可.
这个小Tip是针对一个小工具中出现的问题的一个记录.下篇文章会来聊聊这个小工具:对UITableView高度的cache.
大致思路是:
大部分时候,UITableView的数据源并不会改变.但heightForRowAtIndexPath这个代理,在每一次滑动中均会计算一次高度.实际上是没有必要的.
比较好的做法是在model中自己缓存高度.但是如果有一个UITableview的分类,其中包含一个enableCache的属性,开启后自动缓存是否更好呢?这样子没有通用并且没有侵入性.
所以我们通过一些黑魔法,hook这些代理方法,然后自动缓存,从而得到这个小工具.