iOSruntime之method_exchange

前言

  最近看到工程中对于容器中的生命周期方法hook比较多,有些甚至发生了重复hook的操作。后来组内大佬一同分析之后,无论这个方法背hook了多少次,
  所有被hook的方法都是会一直被执行下去的,我之前听的大概差不多懂了,但是今天还是写个小demo见证下效果。

  一般来讲分类的作用是是给一些系统提供的类别增加一些扩展的功能,这样在可以在不侵入原本代码的基础上,做到能力的扩展。举一个例子,想要按钮有一个点击之后按钮晃动的功能,就可以给UIButton增加一个shaking的分类方法,每次在点击按钮之后调用shaking方法。这样这个shaking的能力相当于独立了出来,很方便能够被复用。

  当然,上面说的都不是重点。iOS方法hook经常也在分类中使用,一般的方式是在+load方法中进行方法交换, load方法在app启动时就会调用,这样可以保证交换的时机尽可能的早。交换之后调用原方法时就相当于调用了交换后的方法,比如下demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
+ (void)load {
Method originalM = class_getInstanceMethod([self class], @selector(viewDidAppear:));
Method destinationM = class_getInstanceMethod([self class], @selector(destinationViewDidAppear:));
BOOL didAddMethod = class_addMethod([self class], @selector(destinationViewDidAppear:), method_getImplementation(destinationM), method_getTypeEncoding(destinationM));
if (!didAddMethod) {
method_exchangeImplementations(originalM, destinationM);
}
}

- (void)destinationViewDidAppear:(BOOL)animated {
NSLog(@"exchange method1,%@",NSStringFromClass([self class]));
[self destinationViewDidAppear:animated];
}

  其中的destinationViewDidAppear就是交换后的方法。注意到这个方法末尾的调用并不是重复调用这个方法,而是去调用了原方法,这里有点乱,应该这么说,交换后的方法我们不会主动调用,而是去调用原本的方法(此时原方法已经被交换),然后调用完之后继续调用交换方法(此时交换的方法已经是原方法)。 整个过程就相当于是你去公司上班的路上顺便买了早饭然后在继续上路。买早饭这个动作就是交换后的方法。做完在回到原本的路径上继续往下。这就大概是方法交换的整个流程。

  多次方法交换后为何每个都继续执行了?下图说明整个过程

图0

图1

图2

 交换完成后就变成了这个样子,所以当执行viewDidAppear时其实执行的是exmethod2,然后method2在调用自身,其实是执行method1,然后method1再调用自身,其实执行的是viewDidAppear。

  也就是说谁先交换,谁就后执行。但是推荐不要这样做,因为每个分类中的load方法执行顺序不确定,所以那个方法先被交换是不确定的。

  但是要注意每次需要交换的方法不要重名,不然就相当于两次交换回到最初的样子了。两个交换后的方法都不会被执行。

代码截图如下:

图3

图4

  执行结果和预期一致。

图5