iOS开发中乱用hook可能导致灾难

今天有同事遇到问题,他重写viewDidAppear:方法,但是,代码并没有执行到。后来我发现,是另个一同事用了黑魔法搞的鬼,而且他本人并不知道这么做会产生影响。(本文中所有黑魔法指Swizzle)

我展示下hook的代码

[self aspect_hookSelector:@selector(viewDidAppear:) withOptions:AspectPositionBefore usingBlock:^(id<AspectInfo> info, BOOL animated) {

        UIViewController *vc = [info instance];

        [vc AspectBeforeViewDidAppear:animated];

    } error:&error];

 

    error = nil;

[self aspect_hookSelector:@selector(viewDidAppear:) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> info, BOOL animated) {

         UIViewController *vc = [info instance];

        [vc AspectAfterViewDidAppear:animated];

    } error:&error];

作者的原意是想在viewDidAppear:执行过程中,在前面执行一些代码,在后面也执行一些代码。魔法的可怕之处来了:难以操控。

 

明白了需求,看看到底问题出在了哪里

 

先做如下指代原函数viewDidAppear:为SEL1,其实现IMP1

原函数执行后的函数SEL3——>IMP3(SEL3)

他设想的两次hook方法实际执行如下。
第一次hook后,
原函数SEL1———>IMP2(SEL2)
原函数执行前的函数SEL2——>IMP1
原函数执行后的函数SEL3——>IMP3(SEL3),
第二次hook,
原函数SEL1———>IMP3(SEL3)
原函数执行前的函数SEL2——>IMP1
原函数执行后的函数SEL3——>IMP2(SEL2),
 
所以最后执行的顺序是调用SEL1寻找到IMP3,执行IMP3的过程中,由于IMP3中会调用SEL3,也就是执行IMP2(如果不明白,请查看swizzle的实现)。原函数的理应被SEL3中调用指向IMP1(SEL2)调用到,但是aspects存在bug,导致IMP2中的SEL2丢失,导致原函数被跳过。aspects的相对而言调用了很多底层函数,合运行时,大部分开发者很难读懂,除了问题也难也解决。
 
最后提出忠告,这种形式的AOP最好只处理简单而统一的业务,这种情况下,的确可能让代码清晰,抽离不想干的逻辑。函数换来换去却是挺绕的,除非你真的知道自己在做什么,否则还是换条路,不然也不好维护,同事可能会出现莫名其妙被捅了一刀的感觉。文笔不太好,再补充点,AOP很容易导致入侵性的代码,最好不要去hook的方法里不要增加对系统sdk属性的操作,你可以自己增加一些属性来操作。以不影响别人的代码

 

posted @ 2015-11-05 10:55  钱鸿强  阅读(3080)  评论(0编辑  收藏  举报