关于不同类的Method Swizzling

2015-12-04  本文已影响214人  oopp

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这些代理方法,然后自动缓存,从而得到这个小工具.

上一篇下一篇

猜你喜欢

热点阅读