lufei's Studio.

初学Method Swizzling

字数统计: 878阅读时长: 3 min
2017/01/09 Share

最近开始学习 Method Swizzling,分享一些学习收获吧。

提到 Method Swizzling,就得提到OC的方法替换了,总结一下,大概应该有这样几种吧:

1.**重写类的方法(Overriding Methods)**:这个就不多说了

2.**伪装(Posing)**:Posing是个很有趣的技术,不过已经过时了,因为64位和iPhone环境下的Objective-C Runtime中不再支持它了. 通过这个伪装(posing),你可子类化,然后将这个子类伪装成它的父类。像变魔术一般,Runtime会让这个子类应用于各处,这时方法复写又有了用处。既然被抛弃了,也就不必多费口舌了

3.归类(Categories):这种方法其实仅仅适用于复写目标类的父类中实现的函数。如果直接复写目标类中的方法,使用归类会带来问题:第一,它无法调用方法的之前的实现。替换掉后,之前的实现就被完全改写了。但大部分情况下,只是想增加些功能,并不期望完全替代。第二,如果被多个category复写,运行时(runtime)并不保证哪个真正会被使用到。

下面就正式进入正题吧,Method Swizzling 解决了这个问题

原理Method Swizzing是发生在运行时的,主要用于在运行时将两个Method进行交换,我们可以将Method Swizzling代码写到任何地方,但是只有在这段Method Swilzzling代码执行完毕之后互换才起作用。
而且Method Swizzling也是iOS中AOP(面相切面编程)的一种实现方式,我们可以利用苹果这一特性来实现AOP编程。

我的理解Method Swizzling的工作原理:原本SEL1对应IMP1SEL2对应IMP2,但是经过Method Swizzing变换后,SEL1对应IMP2SEL2对应IMP1,两个IMP交换一下,从而达到了修改方法的目的。

Method Swizzling使用

在实现Method Swizzling时,核心代码主要就是一个runtime的C语言API:

1
2
 OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

#import "UIViewController+MyMethod Swizzling.h"
#import <objc/runtime.h>

@implementation UIViewController (MyMethod Swizzling)

+ (void)load {

// 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。

Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));

Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingViewDidLoad));

/**

* 我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。

* 而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。

* 所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。

*/

if (!class_addMethod([self class], @selector(swizzlingViewDidLoad), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {

method_exchangeImplementations(fromMethod, toMethod);

}

}

// 我们自己实现的方法,也就是和self的viewDidLoad方法进行交换的方法。

- (void)swizzlingViewDidLoad {

NSString *str = [NSString stringWithFormat:@"%@", self.class];

// 我们在这里加一个判断,将系统的UIViewController的对象剔除掉

if(![str containsString:@"UI"]){

NSLog(@"看看类型 : %@", self.class);

}

[self swizzlingViewDidLoad];

}

@end

注意:刚开始学习,说法什么的用的不准确,也是看的别人的文章,跟着边敲边理解,如果有新的收获,会继续分享的QAQ

参考文章

iOS黑魔法 - Method Swizzling

Runtime Method Swizzling 实践小结

CATALOG