先看第一个问题:分类的加载顺序
举例UIViewController3个分类分别为UIViewController+AUIViewController+BUIViewController+C
.A
1 | + (void)load { |
.B
1 | + (void)load { |
.C
1 | + (void)load { |
运行结果
1 | 2018-03-23 11:06:10.975429+0800 ExchangeImp[4802:195579] ----C--- |
这个顺序是怎么来的的呢,就不卖关子了,本文的重点是第二个问题
其实是这里的顺序,其实压栈的顺序,上面的顺序是C,B,A
我们修改下让以B,A,C输出

运行输出
1 | 2018-03-23 11:09:23.697753+0800 ExchangeImp[4881:201198] ----B--- |
第二个问题:多次使用method_exchangeImplementations同一个方法会是怎样的结果
method_exchangeImplementations关于该方法的使用请自行学习,不是本文的重点
这里我们同时对系统的presentViewController:animated:completion:举例
代码结构
1 | . |
A.m
1 | // |
B.m
1 | // |
C.m
1 | // |
ViewController.m
1 | // |
在build-Phase中的compile source中顺序如下
运行点击后
1 | 2018-03-23 11:25:04.816560+0800 ExchangeImp[5364:228651] ---B-被加载了-- |
发现2个现象:
- 所有的分类方法都被执行了;
- 正好和加载的顺序相反
下面具体解释下这2个现象
在分类A、B、C执行之前2个方法是这样的指向的

在经历分类B以后,变成下面这样

所以在如果没有A和C的话,那么在执行
1 | - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { |
调用顺序为:presentViewController:animated:completion: -> NSLog(@"---B-被执行了--"); -> bAlertPresentViewController:animated:completion:)
所以输出顺序应该是
1 | ---B-被执行了-- |
然后弹出vc
回到正题,那么在执行分类A之前的样子,A方法和presentViewController:animated:completion:指向就是下图
在执行分类A以后,指向如下

所以如果没有分类C的话这时候的输出应该为
1 | ---A-被执行了-- |
继续分析,在分类c执行之前,各个指向为下图

执行以后如下

则执行顺序就很清楚了,到这里也就解释了为何一开始是的输出
1 | 2018-03-23 11:25:04.816560+0800 ExchangeImp[5364:228651] ---B-被加载了-- |
接下来通过logo打印证实上面的观点
改造下之前的代码以B.m为例
1 | // |
执行以后
1 | ExchangeImp[6709:319693] ---B-被加载了-- |
主要看下originalMethod的地址
A的开始bengin的开始也即是originalMethod指向为0x10b6241d0,正是在BB-after中交换后的0x10b6241d0
可以看到每次交换的开始都是上次交换的结果
结论
多次对某个方法进行method_exchangeImplementations所有交换的都会执行到,执行顺序可以自己在build-phase中修改
项目地址
(完)