美文网首页
关于不同类的Method Swizzling

关于不同类的Method Swizzling

作者: oopp | 来源:发表于2015-12-04 17:59 被阅读214次

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

    相关文章

      网友评论

          本文标题:关于不同类的Method Swizzling

          本文链接:https://www.haomeiwen.com/subject/btvshttx.html