将来的自己,会感谢现在努力的自己!

0%

iOS底层探索系列--方法的本质

概述

有下面一份代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import <Foundation/Foundation.h>
#import "FLYPerson.h"

static void run() {

NSLog(@"%s", __func__);
}

int main(int argc, char * argv[]) {
@autoreleasepool {

FLYPerson * person = [FLYPerson alloc];
[person walk];
[FLYPerson say];
run();
}
return 0;
}

用clang编译之后,提取我们想要的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void run() {

NSLog((NSString *)&__NSConstantStringImpl__var_folders_dq_mwrk2yjx1b18hws5lc3pb7g80000gn_T_main_11652b_mi_0, __func__);
}

int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

FLYPerson * person = ((FLYPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("FLYPerson"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("walk"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("FLYPerson"), sel_registerName("say"));
run();
}
return 0;
}

一、runtime

定义 : 一套由c、c++和汇编编写能够为oc提供运行时功能的api。
runtime有两个版本,分别是modern’ 和 ‘legacy’, legacy是老版本中的runtime,Objective-C 2.0 以及后续版本中用的都是modern版本的runtime(参考官方文档)。

使用方式可以归纳为三种:
1、Objective-C code @selector()
2、NSObject的方法 NSSelectorFromString()
3、sel_registerName 函数api

一、objc_msgSend

首先,objc_msgSend是用汇编写的,那么为什么会选用汇编写呢?原因有如下两点

  • 1、因为在C语言中不可能通过写一个函数来保留未知的参数并且跳转到一个任意的函数指针。C语言没有足够做这件事的必要特性。
  • 2、因为objc_msgSend执行的频率是想当非常之高,所以必须保证执行的足够快,这里汇编也就必然是首选。

objc_msgSend可以分为快速路径,慢速路径,都是用C实现的。

作用流程:
1:ENTRY _objc_msgSend
2: 对消息接受者(id self, sel _cmd) 判断处理
3:taggedPointer判断处理
4:‘GetClassFromlsa_p16 isa’ 指针处理 - class
5:CacheLoopup 查找缓存
6:‘cache_t’ 处理 ’bucket’ 以及内存哈希处理
6.1: 找不到递归下一个 ‘bucket’
6.2: 找到了就返回 ‘{imp, sel} = *bucket -> imp’
6.3: 遇到意外就重试
6.4: 找不到就 ‘JumpMiss’
7:__objc_msgSend_uncached 告诉找不到缓存 ‘imp’
8:‘STATIC_ENTRY __objc_msgSend_uncached’
9:‘MethodTableLookup’ 方法表查找
9.1: ‘sava parameter registers’
9.2: ‘self’ 以及 _cmd 准备
9.3: ‘_class_lookupMethodAndLoadCache3’ 调用

下面展示两种用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(int argc, char * argv[]) {
@autoreleasepool {

FLYStudent * person = [FLYStudent alloc];

objc_msgSend(person, sel_registerName("walk"));

//调用对象方法
struct objc_super flySuper;
flySuper.receiver = person;
flySuper.super_class = [person class];//receiver的isa
objc_msgSendSuper(&flySuper, sel_registerName("walk"));

//调用类方法
struct objc_super classSuper;
classSuper.receiver = [person class];
classSuper.super_class = object_getClass([person class]);//receiver的isa(或者为receiver的isa的superClass)
objc_msgSendSuper(&classSuper, sel_registerName("sayXiXi"));
}
return 0;
}
1
2
3
4
5
6

IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/***********************************************************************
* lookUpImpOrForward.
* The standard IMP lookup.
* initialize==NO tries to avoid +initialize (but sometimes fails)
* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)
* Most callers should use initialize==YES and cache==YES.
* inst is an instance of cls or a subclass thereof, or nil if none is known.
* If cls is an un-initialized metaclass then a non-nil inst is faster.
* May return _objc_msgForward_impcache. IMPs destined for external use
* must be converted to _objc_msgForward or _objc_msgForward_stret.
* If you don't want forwarding at all, use lookUpImpOrNil() instead.
**********************************************************************/
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;

runtimeLock.assertUnlocked();

// Optimistic cache lookup
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}

// runtimeLock is held during isRealized and isInitialized checking
// to prevent races against concurrent realization.

// runtimeLock is held during method search to make
// method-lookup + cache-fill atomic with respect to method addition.
// Otherwise, a category could be added but ignored indefinitely because
// the cache was re-filled with the old value after the cache flush on
// behalf of the category.

runtimeLock.lock();
checkIsKnownClass(cls);//检查是否是已知类

/**
如果没有初始化,去初始化该类
*/
if (!cls->isRealized()) {
realizeClass(cls);
}

/**
initialize该类
*/
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.lock();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}


retry:
runtimeLock.assertLocked();

// Try this class's cache.
/**
取缓存,以下为汇编源码,防止被hook,以及运行速度和性能
STATIC_ENTRY _cache_getImp

GetClassFromIsa_p16 p0
CacheLookup GETIMP

LGetImpMiss:
mov p0, #0
ret

END_ENTRY _cache_getImp
*/
imp = cache_getImp(cls, sel);
if (imp) goto done;

/**
查找当前类里面方法列表
*/
// Try this class's method lists.
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}

/**
查找父类里面方法
*/
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}

// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}

// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}


/**
消息转发机制
*/
// No implementation found. Try method resolver once.

if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}

// No implementation found, and method resolver didn't help.
// Use forwarding.

/**
最终都没有找到,走_objc_msgForward_impcache方法,苹果留的最后的后门,
汇编源码:
STATIC_ENTRY __objc_msgForward_impcache

// No stret specialization.
b __objc_msgForward

END_ENTRY __objc_msgForward_impcache

ENTRY __objc_msgForward

adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17

END_ENTRY __objc_msgForward

以上__objc_forward_handler是非汇编
*/
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);

done:
runtimeLock.unlock();

return imp;
}
1
2
3
4
5
6
7
8
9
__attribute__((noreturn)) void 
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]

_class_resolveInstanceMethod(cls, sel, inst);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
_class_resolveClassMethod(cls, sel, inst);
if (!lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
_class_resolveInstanceMethod(cls, sel, inst);
}
}
}
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
static void _class_resolveInstanceMethod(Class cls, SEL sel, id inst)
{
if (! lookUpImpOrNil(cls->ISA(), SEL_resolveInstanceMethod, cls,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}

BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(cls, SEL_resolveInstanceMethod, sel);

// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveInstanceMethod adds to self a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}
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
static void _class_resolveClassMethod(Class cls, SEL sel, id inst)
{
assert(cls->isMetaClass());

if (! lookUpImpOrNil(cls, SEL_resolveClassMethod, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/))
{
// Resolver not implemented.
return;
}

BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(_class_getNonMetaClass(cls, inst),
SEL_resolveClassMethod, sel);

// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
IMP imp = lookUpImpOrNil(cls, sel, inst,
NO/*initialize*/, YES/*cache*/, NO/*resolver*/);

if (resolved && PrintResolving) {
if (imp) {
_objc_inform("RESOLVE: method %c[%s %s] "
"dynamically resolved to %p",
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel), imp);
}
else {
// Method resolver didn't add anything?
_objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
", but no new implementation of %c[%s %s] was found",
cls->nameForLogging(), sel_getName(sel),
cls->isMetaClass() ? '+' : '-',
cls->nameForLogging(), sel_getName(sel));
}
}
}