NSMethodSignature

本文将利用holper disassembler class dump 窥探KVO背后的原理。

CoreFoundation.framework

关于如何提取 CoreFoundation.framework,请参考这里

NSMethodSignature

NSInvocation时,需要方法签名,而NSMethodSignature构造方法如下,
在反编译后,这两个方法其内部实现其实是一致的,如下图

1
2
3
4
5
6
7
8
9


- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
//___methodDescriptionForSelector([rdi class], rdx) != 0x0

+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
//___methodDescriptionForSelector(rdi, rdx) != 0x0

同样的方法,在 ___methodDescriptionForSelector 都是传入的类对象

___methodDescriptionForSelector

长代码警告

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

int ___methodDescriptionForSelector(int arg0, int arg1) {

// arg0:类对象
// arg1:SEL
r15 = arg1; //SEL
var_38 = arg0; //类对象
//非空 去 loc_141cf1
if (arg0 == 0x0) goto loc_141def;

loc_141cf1:
r12 = var_38; //类对象
var_48 = r15; //SEL
goto loc_141d00;

loc_141d00:
// protocolList
// 这里声明了一个空指针传入0x0,
// 如果class_copyProtocolList 有值 会赋在该指针上,类似protertyList用法
rbx = class_copyProtocolList(r12, 0x0); //方法返回:Protocol
// 如果没有protocolList 否则去loc_141d20
if (0x0 == 0x0) goto loc_141da0;

loc_141d20:
// 为上面的 class_copyProtocolList count指针
r13 = 0x0;
//protocol 对象
var_40 = rbx;
goto loc_141d30;

loc_141d30:

//遍历 protocol
r14 = *(rbx + r13 * 0x8);
rax = class_isMetaClass(r12);
rax = protocol_getMethodDescription(r14, r15, 0x1, (rax ^ 0x1) & 0xff);
rdi = r12;
if (rax != 0x0) goto loc_141db0;

loc_141d59:
r14 = *(rbx + r13 * 0x8);
rbx = rdi;
rax = class_isMetaClass(rdi);
rax = protocol_getMethodDescription(r14, r15, 0x0, (rax ^ 0x1) & 0xff);
r14 = 0x0;
if (rax != 0x0) goto loc_141dc0;

loc_141d82:
r13 = r13 + 0x1;
r15 = var_48;
r12 = rbx;
rbx = var_40;
if (r13 < 0x0) goto loc_141d30;

loc_141d98:
r15 = 0x0;
goto loc_141dca;

loc_141dca:
free(rbx);
if (r15 != 0x0) goto loc_141e17;

loc_141dd7:
//获取父类
rax = class_getSuperclass(r12);
r12 = rax; //父类对象
r15 = var_48; //SEL
if (rax != 0x0) goto loc_141d00;

loc_141def:
//如果没有父类,根据 类对象,SEL 获取Method
rax = class_getInstanceMethod(var_38, r15);
if (rax != 0x0) {
//根据Method 获取Description
rax = method_getDescription(rax);
r15 = *rax;
r14 = *(rax + 0x8);
}
else {
r15 = 0x0;
r14 = 0x0;
}
goto loc_141e17;

loc_141e17:
//返回 Description 可能为nil
rax = r15;
return rax;

loc_141dc0:
r15 = rax;
r12 = rbx;
rbx = var_40;
goto loc_141dca;

loc_141db0:
r15 = rax;
r14 = 0x1;
r12 = rdi;
goto loc_141dca;

loc_141da0:
// 如果Protocol 为空 去 loc_141dd7
if (rbx == 0x0) goto loc_141dd7;

loc_141da5:
r14 = 0x0;
r15 = 0x0;
goto loc_141dca;
}

这里面的代码去其实可读性很清晰,下面我们来梳理下调用流程
img

NSInvocation

对于NSInvocation有个疑问

NSInvocation是以什么方式调用的

经过尝试了各种办法之后,我终于得到了一下信息
img
配合反编译的结果
img
我们可以确定,[NSInvocation invoke] 最后还是走了msgSend的过程