iOS中的对象

站在巨人的肩膀,乘后人之凉

分析OC代码,通用手段是用C++ rewrite,通过分析C++代码来了解OC compile time

先来份完整片段

如下OC代码 Alan.m

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
#import <UIKit/UIKit.h>
#import "AppDelegate.h"


@interface Human: NSObject

@end

@implementation Human

- (void)breath {
NSLog(@"fresh air");
}

+ (void)eat {
NSLog(@"raw food");
}

@end

@interface AlanWang: Human

@property NSString *hack;

@end

@implementation AlanWang

- (void)cook {
NSLog(@"then eat");
}

+ (void)swim {
NSLog(@"so much water");
}

@end


int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
AlanWang *man = [AlanWang alloc];
man = [man init];
@autoreleasepool {
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

rewrite xcrun -sdk iphoneos clang -rewrite-objc -F UIKit -fobjc-arc -arch arm64 Alan.m

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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319



#ifndef _REWRITER_typedef_Human
#define _REWRITER_typedef_Human
typedef struct objc_object Human;
typedef struct {} _objc_exc_Human;
#endif

struct Human_IMPL {
struct NSObject_IMPL NSObject_IVARS;
};


/* @end */


// @implementation Human


static void _I_Human_breath(Human * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_1f_vycdcqqn5r32n45pwpqrj1gr0000gn_T_Alan_02f76f_mi_0);
}


static void _C_Human_eat(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_1f_vycdcqqn5r32n45pwpqrj1gr0000gn_T_Alan_02f76f_mi_1);
}

// @end


#ifndef _REWRITER_typedef_AlanWang
#define _REWRITER_typedef_AlanWang
typedef struct objc_object AlanWang;
typedef struct {} _objc_exc_AlanWang;
#endif

extern "C" unsigned long OBJC_IVAR_$_AlanWang$_hack;
struct AlanWang_IMPL {
struct Human_IMPL Human_IVARS;
NSString *__strong _hack;
};


// @property NSString *hack;

/* @end */


// @implementation AlanWang


static void _I_AlanWang_cook(AlanWang * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_1f_vycdcqqn5r32n45pwpqrj1gr0000gn_T_Alan_02f76f_mi_2);
}


static void _C_AlanWang_swim(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_1f_vycdcqqn5r32n45pwpqrj1gr0000gn_T_Alan_02f76f_mi_3);
}


static NSString * _I_AlanWang_hack(AlanWang * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_AlanWang$_hack)); }
static void _I_AlanWang_setHack_(AlanWang * self, SEL _cmd, NSString *hack) { (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_AlanWang$_hack)) = hack; }
// @end


int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
AlanWang *man = ((AlanWang *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AlanWang"), sel_registerName("alloc"));
man = ((AlanWang *(*)(id, SEL))(void *)objc_msgSend)((id)man, sel_registerName("init"));
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class")));
}
return UIApplicationMain(argc, argv, __null, appDelegateClassName);
}

struct _prop_t {
const char *name;
const char *attributes;
};

struct _protocol_t;

struct _objc_method {
struct objc_selector * _cmd;
const char *method_type;
void *_imp;
};

struct _protocol_t {
void * isa; // NULL
const char *protocol_name;
const struct _protocol_list_t * protocol_list; // super protocols
const struct method_list_t *instance_methods;
const struct method_list_t *class_methods;
const struct method_list_t *optionalInstanceMethods;
const struct method_list_t *optionalClassMethods;
const struct _prop_list_t * properties;
const unsigned int size; // sizeof(struct _protocol_t)
const unsigned int flags; // = 0
const char ** extendedMethodTypes;
};

struct _ivar_t {
unsigned long int *offset; // pointer to ivar offset location
const char *name;
const char *type;
unsigned int alignment;
unsigned int size;
};

struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
const unsigned char *ivarLayout;
const char *name;
const struct _method_list_t *baseMethods;
const struct _objc_protocol_list *baseProtocols;
const struct _ivar_list_t *ivars;
const unsigned char *weakIvarLayout;
const struct _prop_list_t *properties;
};

struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};

struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
extern "C" __declspec(dllimport) struct objc_cache _objc_empty_cache;
#pragma warning(disable:4273)

static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_INSTANCE_METHODS_Human __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"breath", "v16@0:8", (void *)_I_Human_breath}}
};

static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_Human __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"eat", "v16@0:8", (void *)_C_Human_eat}}
};

static struct _class_ro_t _OBJC_METACLASS_RO_$_Human __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1, sizeof(struct _class_t), sizeof(struct _class_t),
0,
"Human",
(const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_Human,
0,
0,
0,
0,
};

static struct _class_ro_t _OBJC_CLASS_RO_$_Human __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0, sizeof(struct Human_IMPL), sizeof(struct Human_IMPL),
0,
"Human",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_Human,
0,
0,
0,
0,
};

extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;

extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_Human __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_NSObject,
0, // &OBJC_METACLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_METACLASS_RO_$_Human,
};

extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Human __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_Human,
0, // &OBJC_CLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_Human,
};
static void OBJC_CLASS_SETUP_$_Human(void ) {
OBJC_METACLASS_$_Human.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Human.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Human.cache = &_objc_empty_cache;
OBJC_CLASS_$_Human.isa = &OBJC_METACLASS_$_Human;
OBJC_CLASS_$_Human.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_Human.cache = &_objc_empty_cache;
}

extern "C" unsigned long int OBJC_IVAR_$_AlanWang$_hack __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct AlanWang, _hack);

static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[1];
} _OBJC_$_INSTANCE_VARIABLES_AlanWang __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
1,
{{(unsigned long int *)&OBJC_IVAR_$_AlanWang$_hack, "_hack", "@\"NSString\"", 3, 8}}
};

static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[3];
} _OBJC_$_INSTANCE_METHODS_AlanWang __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
3,
{{(struct objc_selector *)"cook", "v16@0:8", (void *)_I_AlanWang_cook},
{(struct objc_selector *)"hack", "@16@0:8", (void *)_I_AlanWang_hack},
{(struct objc_selector *)"setHack:", "v24@0:8@16", (void *)_I_AlanWang_setHack_}}
};

static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CLASS_METHODS_AlanWang __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"swim", "v16@0:8", (void *)_C_AlanWang_swim}}
};

static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_AlanWang __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"hack","T@\"NSString\",&,V_hack"}}
};

static struct _class_ro_t _OBJC_METACLASS_RO_$_AlanWang __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1, sizeof(struct _class_t), sizeof(struct _class_t),
0,
"AlanWang",
(const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_AlanWang,
0,
0,
0,
0,
};

static struct _class_ro_t _OBJC_CLASS_RO_$_AlanWang __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0, __OFFSETOFIVAR__(struct AlanWang, _hack), sizeof(struct AlanWang_IMPL),
0,
"AlanWang",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_AlanWang,
0,
(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_AlanWang,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_AlanWang,
};

extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_Human;
extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;

extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_AlanWang __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_NSObject,
0, // &OBJC_METACLASS_$_Human,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_METACLASS_RO_$_AlanWang,
};

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Human;

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_AlanWang __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_AlanWang,
0, // &OBJC_CLASS_$_Human,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_AlanWang,
};
static void OBJC_CLASS_SETUP_$_AlanWang(void ) {
OBJC_METACLASS_$_AlanWang.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_AlanWang.superclass = &OBJC_METACLASS_$_Human;
OBJC_METACLASS_$_AlanWang.cache = &_objc_empty_cache;
OBJC_CLASS_$_AlanWang.isa = &OBJC_METACLASS_$_AlanWang;
OBJC_CLASS_$_AlanWang.superclass = &OBJC_CLASS_$_Human;
OBJC_CLASS_$_AlanWang.cache = &_objc_empty_cache;
}
#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CLASS_SETUP[] = {
(void *)&OBJC_CLASS_SETUP_$_Human,
(void *)&OBJC_CLASS_SETUP_$_AlanWang,
};
static struct _class_t *L_OBJC_LABEL_CLASS_$ [2] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= {
&OBJC_CLASS_$_Human,
&OBJC_CLASS_$_AlanWang,
};
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

可以搜索关键字AlanWang来定位代码,当然往往都在最下面

不难发现c++重写后OC类被加了前缀

OBJC_CLASS_REF_$_

OBJC_CLASS_$_

OBJC_METACLASS_$_

查阅Swift源码可见的确是这些前缀。可见在rewrite过程中总会被加一些标识前缀,所以阅读时自行转化前缀

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
// An Objective-C class reference reference. The symbol is private, so
// the mangling is unimportant; it should just be readable in LLVM IR.
case Kind::ObjCClassRef: {
llvm::SmallString<64> tempBuffer;
StringRef name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(tempBuffer);
std::string Result("OBJC_CLASS_REF_$_");
Result.append(name.data(), name.size());
return Result;
}

// An Objective-C class reference; not a swift mangling.
case Kind::ObjCClass: {
llvm::SmallString<64> TempBuffer;
StringRef Name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(TempBuffer);
std::string Result("OBJC_CLASS_$_");
Result.append(Name.data(), Name.size());
return Result;
}

// An Objective-C metaclass reference; not a swift mangling.
case Kind::ObjCMetaclass: {
llvm::SmallString<64> TempBuffer;
StringRef Name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(TempBuffer);
std::string Result("OBJC_METACLASS_$_");
Result.append(Name.data(), Name.size());
return Result;
}

先来看两个类的初始化方法

extern “C” 的意思是把它当做C语言编译

比如C++支持重载,而C不支持

1
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Human;
1
2
3
4
5
6
7
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_AlanWang __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_AlanWang,
0, // &OBJC_CLASS_$_Human,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_AlanWang,
};

AlanWang 这个类被存在"__DATA, __objc_classlist,regular,no_dead_strip"section中

_class_t
1
2
3
4
5
6
7
struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};

_class_t 的 isa 和 superclass 指向 _class_t

SETUP AlanWang
1
2
3
4
5
6
7
8
static void OBJC_CLASS_SETUP_$_AlanWang(void ) {
OBJC_METACLASS_$_AlanWang.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_AlanWang.superclass = &OBJC_METACLASS_$_Human;
OBJC_METACLASS_$_AlanWang.cache = &_objc_empty_cache;
OBJC_CLASS_$_AlanWang.isa = &OBJC_METACLASS_$_AlanWang;
OBJC_CLASS_$_AlanWang.superclass = &OBJC_CLASS_$_Human;
OBJC_CLASS_$_AlanWang.cache = &_objc_empty_cache;
}
SETUP Human
1
2
3
4
5
6
7
8
static void OBJC_CLASS_SETUP_$_Human(void ) {
OBJC_METACLASS_$_Human.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Human.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_Human.cache = &_objc_empty_cache;
OBJC_CLASS_$_Human.isa = &OBJC_METACLASS_$_Human;
OBJC_CLASS_$_Human.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_Human.cache = &_objc_empty_cache;
}

一图胜千言。

OC中类的关系图

_class_ro_t
1
2
3
4
5
6
7
8
9
10
11
12
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
const unsigned char *ivarLayout;
const char *name;
const struct _method_list_t *baseMethods;
const struct _objc_protocol_list *baseProtocols;
const struct _ivar_list_t *ivars;
const unsigned char *weakIvarLayout;
const struct _prop_list_t *properties;
};

ro readonly

1
2
3
4
5
struct _objc_method {
struct objc_selector * _cmd;
const char *method_type;
void *_imp;
};

通观全文,不难发现并未找到alloc方法的声明与实现

main函数中的实现

1
2
3
4
5
6
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
AlanWang *man = ((AlanWang *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AlanWang"), sel_registerName("alloc"));
man = ((AlanWang *(*)(id, SEL))(void *)objc_msgSend)((id)man, sel_registerName("init"));
...
}

到运行时dyld通知回调时执行map_images时才会注册alloc方法

Type Encodings

运行时体系结构(Runtime Architecture)

具有关键的一些方面

  • 提供了用于启动和执行程序的工具
  • 指定了代码和数据怎样驻留在磁盘上—-也就是说,它制定了二进制格式。它还指定了编译器和相关工具必须怎样生成代码和数据
  • 指定了怎样将代码和数据加载进内存中
  • 指定了怎样解析指向外部库的引用

Mac OS X & iOS 运行时环境: Mach-O

对象的诞生

objc_getClass

1
2
3
4
5
6
7
8
9
10
11
12
13
/***********************************************************************
* objc_getClass. Return the id of the named class. If the class does
* not exist, call _objc_classLoader and then objc_classHandler, either of
* which may create a new class.
* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
**********************************************************************/
Class objc_getClass(const char *aClassName)
{
if (!aClassName) return Nil;

// NO unconnected, YES class handler
return look_up_class(aClassName, NO, YES);
}

提供类名到类映射的哈希表

1
2
3
// Maps class name to Class, for in-use classes only. NXStrValueMapPrototype.
OBJC_EXPORT NXMapTable * _Nullable gdb_objc_realized_classes
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static Class getClass_impl(const char *name)
{
runtimeLock.assertLocked();

// allocated in _read_images
assert(gdb_objc_realized_classes);

// Try runtime-allocated table
Class result = (Class)NXMapGet(gdb_objc_realized_classes, name);
if (result) return result;

// Try table from dyld shared cache
return getPreoptimizedClass(name);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/***********************************************************************
* _read_images
* Perform initial processing of the headers in the linked
* list beginning with headerList.
*
* Called by: map_images_nolock
*
* Locking: runtimeLock acquired by map_images
**********************************************************************/
void objc_getClass_read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
...
readClass
...

_objc_init–>map_images –> map_images_nolock –> _read_images

objc_getClass:通过key= className 在哈希表(gdb_objc_realized_classes)找到 Class

sel_registerName:通过key= selectorName 在哈希表(namedSelectors )找到SEL

objc_msgSend: 参数为Class 与 SEL

objc_msgSend伪代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/********************************************************************
*
* id objc_msgSend(id self, SEL _cmd, ...);
* IMP objc_msgLookup(id self, SEL _cmd, ...);
*
* objc_msgLookup ABI:
* IMP returned in x17
* x16 reserved for our use but not used
*
********************************************************************/

id objc_msgSend(id self, SEL _cmd, ...) {
Class class = object_getClass(self);
IMP imp = class_getMethodImplementation(class, _cmd);
return imp ? imp(self, _cmd, ...) : 0;
}

在cache中找

在类的方法列表中找

消息转发

终于找到alloc方法了

运行时类的结构

objc源代码下载

目前最新的是objc4-779.1

objc.h

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
/*
* objc.h
* Copyright 1988-1996, NeXT Software, Inc.
*/

#if !OBJC_TYPES_DEFINED
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;
#endif

/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;

/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
#endif

Class 意为一个不透明的类型(指针) 代表一个Objective-C的类

id 意为类的实例的指针

SEL 意为一个不透明的类型 代表一个方法选择器

IMP 意为方法实现的函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
* objc-api.h
*/

/* OBJC_OLD_DISPATCH_PROTOTYPES == 0 enforces the rule that the dispatch
* functions must be cast to an appropriate function pointer type. */
#if !defined(OBJC_OLD_DISPATCH_PROTOTYPES)
# if __swift__
// Existing Swift code expects IMP to be Comparable.
// Variadic IMP is comparable via OpaquePointer; non-variadic IMP isn't.
# define OBJC_OLD_DISPATCH_PROTOTYPES 1
# else
# define OBJC_OLD_DISPATCH_PROTOTYPES 0
# endif
#endif

如果有swift的代码。则可变的IMP是不透明指针,不可变则不是。

OBJC_TYPES_DEFINED` 宏定义是因为在另一处

objc-private.h中也有声明 避免重复定义

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
/*
* objc-private.h
* Copyright 1988-1996, NeXT Software, Inc.
*/

#define OBJC_TYPES_DEFINED 1

struct objc_class;
struct objc_object;

typedef struct objc_class *Class;
typedef struct objc_object *id;

namespace {
struct SideTable;
};

#include "isa.h"

union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }

Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
...
Meta 元类

objc-config.h中的宏

__LP64__

字长为64位的操作系统

在 XL C/C++ V10.1 中添加了四个新宏:

1
2
3
4
➜  / clang -dM -E -x c /dev/null
#define OBJC_NEW_PROPERTIES 1
#define _LP64 1
...
__arm__

32-bit ARM

1
2
3
4
5
6
7
8
9
10
#if __PTRAUTH_INTRINSICS__
// Always use ptrauth when it's supported.
#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_PTRAUTH
#elif defined(__arm__)
// 32-bit ARM uses no encoding.
#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_NONE
#else
// Everything else uses ISA ^ IMP.
#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_ISA_XOR
#endif
SUPPORT_PACKED_ISA

isa 视为被数据包裹的可以掩码化的指针。

1
2
3
4
5
6
7
8
// Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa 
// field as a maskable pointer with other data around it.
#if (!__LP64__ || TARGET_OS_WIN32 || \
(TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC))
# define SUPPORT_PACKED_ISA 0
#else
# define SUPPORT_PACKED_ISA 1
#endif

iOS中为1

SUPPORT_TAGGED_POINTERS
1
2
3
4
5
6
7
// Define SUPPORT_TAGGED_POINTERS=1 to enable tagged pointer objects
// Be sure to edit tagged pointer SPI in objc-internal.h as well.
#if !(__OBJC2__ && __LP64__)
# define SUPPORT_TAGGED_POINTERS 0
#else
# define SUPPORT_TAGGED_POINTERS 1
#endif

iOS中为1

SUPPORT_INDEXED_ISA
1
2
3
4
5
6
7
8
9
// Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa 
// field as an index into a class table.
// Note, keep this in sync with any .s files which also define it.
// Be sure to edit objc-abi.h as well.
#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
# define SUPPORT_INDEXED_ISA 1
#else
# define SUPPORT_INDEXED_ISA 0
#endif

iOS中为0

(__arm64__ && !__LP64__) 这个条件为了兼容32位系统?

SUPPORT_PACKED_ISA
1
2
3
4
5
6
7
8
// Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa 
// field as a maskable pointer with other data around it.
#if (!__LP64__ || TARGET_OS_WIN32 || \
(TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC))
# define SUPPORT_PACKED_ISA 0
#else
# define SUPPORT_PACKED_ISA 1
#endif

iOS中为1

SUPPORT_NONPOINTER_ISA

可能在isa中存有额外的数据,支持所有平台

1
2
3
4
5
6
7
// Define SUPPORT_NONPOINTER_ISA=1 on any platform that may store something
// in the isa field that is not a raw pointer.
#if !SUPPORT_INDEXED_ISA && !SUPPORT_PACKED_ISA
# define SUPPORT_NONPOINTER_ISA 0
#else
# define SUPPORT_NONPOINTER_ISA 1
#endif
SUPPORT_FIXUP
1
2
3
4
5
6
7
8
// Define SUPPORT_FIXUP=1 to repair calls sites for fixup dispatch.
// Fixup messaging itself is no longer supported.
// Be sure to edit objc-abi.h as well (objc_msgSend*_fixup)
#if !(defined(__x86_64__) && (TARGET_OS_OSX || TARGET_OS_SIMULATOR))
# define SUPPORT_FIXUP 0
#else
# define SUPPORT_FIXUP 1
#endif
SUPPORT_ZEROCOST_EXCEPTIONS
1
2
3
4
5
6
7
// Define SUPPORT_ZEROCOST_EXCEPTIONS to use "zero-cost" exceptions for OBJC2.
// Be sure to edit objc-exception.h as well (objc_add/removeExceptionHandler)
#if !__OBJC2__ || (defined(__arm__) && __USING_SJLJ_EXCEPTIONS__)
# define SUPPORT_ZEROCOST_EXCEPTIONS 0
#else
# define SUPPORT_ZEROCOST_EXCEPTIONS 1
#endif
ISA_BITFIELD, RC_ONE, RC_HALF

前文提到iOS中SUPPORT_PACKED_ISA为1,所以iOS64位机中isa的相关字段定义如下

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
#if SUPPORT_PACKED_ISA

// extra_rc must be the MSB-most field (so it matches carry/overflow flags)
// nonpointer must be the LSB (fixme or get rid of it)
// shiftcls must occupy the same bits that a real class pointer would
// bits + RC_ONE is equivalent to extra_rc + 1
// RC_HALF is the high bit of extra_rc (i.e. half of its range)

// future expansion:
// uintptr_t fast_rr : 1; // no r/r overrides
// uintptr_t lock : 2; // lock for atomic property, @synch
// uintptr_t extraBytes : 1; // allocated with extra bytes
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)

MSB: most significant bit 最高有效位(最左位)

LSB: least significant bit 最低有效位

ISA_MAGIC_VALUE

0x000001a000000001ULL

TaggedPointer
1
2
3
4
5
6
7
#if (TARGET_OS_OSX || TARGET_OS_IOSMAC) && __x86_64__
// 64-bit Mac - tag bit is LSB
# define OBJC_MSB_TAGGED_POINTERS 0
#else
// Everything else - tag bit is MSB
# define OBJC_MSB_TAGGED_POINTERS 1
#endif

只有64位 Mac 上是LSB 也就是看最低位

iOS中看MSB 也就是最高位(第63位)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#if OBJC_MSB_TAGGED_POINTERS
# define _OBJC_TAG_MASK (1UL<<63)
# define _OBJC_TAG_INDEX_SHIFT 60
# define _OBJC_TAG_SLOT_SHIFT 60
# define _OBJC_TAG_PAYLOAD_LSHIFT 4
# define _OBJC_TAG_PAYLOAD_RSHIFT 4
# define _OBJC_TAG_EXT_MASK (0xfUL<<60)
# define _OBJC_TAG_EXT_INDEX_SHIFT 52
# define _OBJC_TAG_EXT_SLOT_SHIFT 52
# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 12
# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
#else
# define _OBJC_TAG_MASK 1UL
# define _OBJC_TAG_INDEX_SHIFT 1
# define _OBJC_TAG_SLOT_SHIFT 0
# define _OBJC_TAG_PAYLOAD_LSHIFT 0
# define _OBJC_TAG_PAYLOAD_RSHIFT 4
# define _OBJC_TAG_EXT_MASK 0xfUL
# define _OBJC_TAG_EXT_INDEX_SHIFT 4
# define _OBJC_TAG_EXT_SLOT_SHIFT 4
# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 0
# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12
#endif

流程

libdispatch_init -> _os_object_init -> _objc_init —[_dyld_objc_notify_register(&map_images, load_images, unmap_image)] —>

dyld 通知 -> (map_images,load_images,unmap_image)

map_images->arr_init->

AutoreleasePoolPage::init(); -> 注册析构到对应线程

SideTableInit();->

realizeClassWithoutSwift

addRootClass(cls);

1
2
3
4
5
6
7
8
9
10
11
12
# 没有supercls的根类
(Class) $0 = NSObject
(Class) $1 = __NSAtom
(Class) $2 = NSProxy
-<main>-
(Class) $3 = Object
(Class) $4 = __NSMessageBuilder
(Class) $5 = _NSZombie_
(Class) $6 = __NSGenericDeallocHandler
(Class) $7 = NSLeafProxy
(Class) $8 = JSExport
(Class) $9 = _CNZombie_

调用堆栈

从dyld进入到libSystem

先看ImageLoaderMachO::doModInitFunctions

1
2
Initializer* inits = (Initializer*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uintptr_t);

通过这两句得到libSystem.B.dylib__mod_init_funcssection inits数组,元素类型为Initializer* 函数指针类型

1
2
// dyld/src/ImageLoader.h
typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars);

otool -l /usr/lib/libSystem.dylib查看libSystem依赖的库

libSystem.dylib会加载libSystem.B.dylib

1
2
3
4
5
6
7
8
9
10
➜  ~  otool -L  /usr/lib/libSystem.dylib
/usr/lib/libSystem.dylib:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)
/usr/lib/system/libcache.dylib (compatibility version 1.0.0, current version 81.0.0)
/usr/lib/system/libcommonCrypto.dylib (compatibility version 1.0.0, current version 60118.250.2)
/usr/lib/system/libcompiler_rt.dylib (compatibility version 1.0.0, current version 63.4.0)
/usr/lib/system/libcopyfile.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/system/libcorecrypto.dylib (compatibility version 1.0.0, current version 602.255.2)
/usr/lib/system/libdispatch.dylib (compatibility version 1.0.0, current version 1008.255.1)
...

查看load commands

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  ~ otool -l /usr/lib/libSystem.B.dylib
/usr/lib/libSystem.B.dylib:
...
Section
sectname __mod_init_func
segname __DATA
addr 0x0000000000002218
size 0x0000000000000008
offset 8728
align 2^3 (8)
reloff 0
nreloc 0
flags 0x00000009
reserved1 0
reserved2 0
...

otool -l /usr/lib/libSystem.dylib`

1
2
3
4
5
6
7
8
9
10
11
12
Section
sectname __mod_init_func
segname __DATA
addr 0x0000000000002218
size 0x0000000000000008
offset 8728
align 2^3 (8)
reloff 0
nreloc 0
flags 0x00000009
reserved1 0
reserved2 0

调用数组里的函数

1
2
3
Initializer func = inits[j];

func(context.argc, context.argv, context.envp, context.apple, &context.programVars);

汇编片段

1
2
3
4
5
  0x1090833a0 <+510>:  movq   -0x68(%rbp), %r8
0x1090833a4 <+514>: callq *%r13
-> 0x1090833a7 <+517>: testq %r14, %r14
0x1090833aa <+520>: jne 0x1090833c9 ; <+551>
0x1090833ac <+522>: leaq 0x42e45(%rip), %rax ; dyld::gLibSystemHelpers

此时控制台

1
2
3
(lldb) register read r13

r13 = 0x00007fff4ff16773 libSystem.B.dylib`libSystem_initializer

如果设置了DYLD_PRINT_INITIALIZERS变量,还会打印

1
dyld: calling initializer function 0x7fff4ff16773 in /usr/lib/libSystem.B.dylib

对应代码

1
2
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());

小结:ImageLoaderMachO::doModInitFunctions方法中找到__mod_init_funcs section,通过地址加fSlide计算得到inits数组,然后调用libSystem.B.dylib`libSystem_initializer

ImageLoaderMachO::doModInitFunctions相关代码如下

SECTION_TYPE

1
2
3
4
5
6
7
8
/*
* The flags field of a section structure is separated into two parts a section
* type and section attributes. The section types are mutually exclusive (it
* can only have one type) but the section attributes are not (it may have more
* than one attribute).
*/
#define SECTION_TYPE 0x000000ff /* 256 section types */
#define SECTION_ATTRIBUTES 0xffffff00 /* 24 section attributes */

S_MOD_INIT_FUNC_POINTERS

1
2
3
// darwin-xnu/EXTERNAL_HEADERS/mach-o/loader.h
#define S_MOD_INIT_FUNC_POINTERS 0x9 /* section with only function
pointers for initialization*/
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
void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
{
if ( fHasInitializers ) {
const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
const struct load_command* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects];
for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
// #define SECTION_TYPE 0x000000ff
// 取sect->flags后两位,意为section type
const uint8_t type = sect->flags & SECTION_TYPE;
// #define S_MOD_INIT_FUNC_POINTERS 0x9
if ( type == S_MOD_INIT_FUNC_POINTERS ) {
Initializer* inits = (Initializer*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uintptr_t);
// <rdar://problem/23929217> Ensure __mod_init_func section is within segment
if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
dyld::throwf("__mod_init_funcs section has malformed address range for %s\n", this->getPath());
for (size_t j=0; j < count; ++j) {
Initializer func = inits[j];
// <rdar://problem/8543820&9228031> verify initializers are in image
if ( ! this->containsAddress(stripPointer((void*)func)) ) {
dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath());
}
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// <rdar://problem/17973316> libSystem initializer must run first
const char* installPath = getInstallPath();
if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )
dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
{
dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
}
bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
// now safe to use malloc() and other calls in libSystem.dylib
dyld::gProcessInfo->libSystemInitialized = true;
}
}
}
else if ( type == S_INIT_FUNC_OFFSETS ) {
const uint32_t* inits = (uint32_t*)(sect->addr + fSlide);
const size_t count = sect->size / sizeof(uint32_t);
// Ensure section is within segment
if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) )
dyld::throwf("__init_offsets section has malformed address range for %s\n", this->getPath());
if ( seg->initprot & VM_PROT_WRITE )
dyld::throwf("__init_offsets section is not in read-only segment %s\n", this->getPath());
for (size_t j=0; j < count; ++j) {
uint32_t funcOffset = inits[j];
// verify initializers are in TEXT segment
if ( funcOffset > seg->filesize ) {
dyld::throwf("initializer function offset 0x%08X not in mapped image for %s\n", funcOffset, this->getPath());
}
if ( ! dyld::gProcessInfo->libSystemInitialized ) {
// <rdar://problem/17973316> libSystem initializer must run first
const char* installPath = getInstallPath();
if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) )
dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath());
}
Initializer func = (Initializer)((uint8_t*)this->machHeader() + funcOffset);
if ( context.verboseInit )
dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL);
{
dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
}
bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL);
if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) {
// now safe to use malloc() and other calls in libSystem.dylib
dyld::gProcessInfo->libSystemInitialized = true;
}
}
}
}
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}

ImageLoaderMachO::doModInitFunctions->libSystem.B.dylib`libSystem_initializer

从libSystem到libSystem

1
libSystem.B.dylib`libSystem_initializer->libdispatch.dylib`libdispatch_init

_os_object_init

1
2
3
4
5
6
7
8
9
10
11
void
libdispatch_init(void)
{
...
_dispatch_hw_config_init();
_dispatch_time_init();
_dispatch_vtable_init();
_os_object_init();
_voucher_init();
_dispatch_introspection_init();
}

libdispatch_init中, dispatch进行一系列的初始化后,调用_os_object_init。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void
_os_object_init(void)
{
_objc_init();
Block_callbacks_RR callbacks = {
sizeof(Block_callbacks_RR),
(void (*)(const void *))&objc_retain,
(void (*)(const void *))&objc_release,
(void (*)(const void *))&_os_objc_destructInstance
};
_Block_use_RR2(&callbacks);
#if DISPATCH_COCOA_COMPAT
const char *v = getenv("OBJC_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
v = getenv("DISPATCH_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
v = getenv("LIBDISPATCH_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
#endif
}

_os_object_init->_objc_init

从libdispatch进入到objc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/

void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;

// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();

_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

进行一系列初始化

注册三个方法到dyld

registerObjCNotifiers->notifyBatchPartial

objc 接收到通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/***********************************************************************
* map_images
* Process the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock
**********************************************************************/
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void 
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
...
// Perform one-time runtime initialization that must be deferred until
// the executable itself is found. This needs to be done before
// further initialization.
// (The executable may not be present in this infoList if the
// executable does not contain Objective-C code but Objective-C
// is dynamically loaded later.
if (firstTime) {
sel_init(selrefCount);
arr_init();

...
}
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
...
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
/***********************************************************************
* sel_init
* Initialize selector tables and register selectors used internally.
**********************************************************************/
void sel_init(size_t selrefCount)
{
...
// Register selectors used by libobjc

#define s(x) SEL_##x = sel_registerNameNoLock(#x, NO)
#define t(x,y) SEL_##y = sel_registerNameNoLock(#x, NO)

mutex_locker_t lock(selLock);

s(load);
s(initialize);
t(resolveInstanceMethod:, resolveInstanceMethod);
t(resolveClassMethod:, resolveClassMethod);
t(.cxx_construct, cxx_construct);
t(.cxx_destruct, cxx_destruct);
s(retain);
s(release);
s(autorelease);
s(retainCount);
s(alloc);
t(allocWithZone:, allocWithZone);
s(dealloc);
s(copy);
s(new);
t(forwardInvocation:, forwardInvocation);
t(_tryRetain, tryRetain);
t(_isDeallocating, isDeallocating);
s(retainWeakReference);
s(allowsWeakReference);

#undef s
#undef t
}
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
SEL sel_registerNameNoLock(const char *name, bool copy) {
return __sel_registerName(name, 0, copy); // NO lock, maybe copy
}
static NXMapTable *namedSelectors;
static SEL __sel_registerName(const char *name, bool shouldLock, bool copy)
{
SEL result = 0;
...
if (namedSelectors) {
result = (SEL)NXMapGet(namedSelectors, name);
}
if (result) return result;

// No match. Insert.

if (!namedSelectors) {
// 新建哈希表
namedSelectors = NXCreateMapTable(NXStrValueMapPrototype,
(unsigned)SelrefCount);
}
if (!result) {
// 查表没有命中,则插入
result = sel_alloc(name, copy);
// fixme choose a better container (hash not map for starters)
NXMapInsert(namedSelectors, sel_getName(result), result);
}

return result;
}

sel_init中的方法注册到全局静态哈希表namedSelectors,数据结构定义如下

1
2
3
4
5
6
7
8
typedef struct {
const NXHashTablePrototype * _Nonnull prototype OBJC_HASH_AVAILABILITY;
unsigned count OBJC_HASH_AVAILABILITY;
unsigned nbBuckets OBJC_HASH_AVAILABILITY;
void * _Nullable buckets OBJC_HASH_AVAILABILITY;
const void * _Nullable info OBJC_HASH_AVAILABILITY;
} NXHashTable OBJC_HASH_AVAILABILITY;
/* private data structure; may change */

建表过程会调用到bootstrap

1
2
3
4
5
6
7
8
9
10
11
static void bootstrap (void) {
free(malloc(8));
prototypes = ALLOCTABLE (DEFAULT_ZONE);
prototypes->prototype = &protoPrototype;
prototypes->count = 1;
prototypes->nbBuckets = 1; /* has to be 1 so that the right bucket is 0 */
prototypes->buckets = ALLOCBUCKETS(DEFAULT_ZONE, 1);
prototypes->info = NULL;
((HashBucket *) prototypes->buckets)[0].count = 1;
((HashBucket *) prototypes->buckets)[0].elements.one = &protoPrototype;
};
arr_init
1
2
3
4
5
void arr_init(void) 
{
AutoreleasePoolPage::init();
SideTableInit();
}

初始化AutoreleasePoolPage

初始化SideTable

_read_images
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
/***********************************************************************
* _read_images
* Perform initial processing of the headers in the linked
* list beginning with headerList.
*
* Called by: map_images_nolock
*
* Locking: runtimeLock acquired by map_images
**********************************************************************/
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
...
// Realize non-lazy classes (for +load methods and static instances)
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;

// hack for class __ARCLite__, which didn't get this above
...
addClassTableEntry(cls);

if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
realizeClassWithoutSwift(cls);
}
}
...
}
releaize

先看主体流程

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
/***********************************************************************
* realizeClassWithoutSwift
* Performs first-time initialization on class cls,
* including allocating its read-write data.
* Does not perform any Swift-side initialization.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
static Class realizeClassWithoutSwift(Class cls)
{
...0
//MARK: - 自注解 NSObject递归结束条件
if (!cls) return nil;
//MARK: - 自注解 子类,元类结束条件
if (cls->isRealized()) return cls;
...1
// Realize superclass and metaclass, if they aren't already.
// This needs to be done after RW_REALIZED is set above, for root classes.
// This needs to be done after class index is chosen, for root metaclasses.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
//MARK: - 自注解 递归确保根节点realize
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
...2
// Update superclass and metaclass in case of remapping
cls->superclass = supercls;
cls->initClassIsa(metacls);
...3
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
//MARK: - 自注解 添加根类
addRootClass(cls);
}

// Attach categories
methodizeClass(cls);

return cls;
}
1
2
3
4
5
6
7
8
9
10
11
12
// addRootClass(cls); cls包含的类
(Class) $0 = NSObject
(Class) $1 = __NSAtom
(Class) $2 = NSProxy
<main>
(Class) $3 = Object
(Class) $4 = __NSMessageBuilder
(Class) $5 = _NSZombie_
(Class) $6 = __NSGenericDeallocHandler
(Class) $7 = NSLeafProxy
(Class) $8 = JSExport
(Class) $9 = _CNZombie_

可以看到,main函数前,有三个cls,NSObject, __NSAtom, NSProxy

1
2
3
4
5
6
7
8
static void addRootClass(Class cls)
{
runtimeLock.assertLocked();

assert(cls->isRealized());
cls->data()->nextSiblingClass = _firstRealizedClass;
_firstRealizedClass = cls;
}

addRootClass 根类数据结构的调整,可以发现为单链表,每一次把新结点通过头插法插入到表头(_firstRealizedClass)

1
2
3
4
5
6
7
8
9
10
11
12
/***********************************************************************
* addSubclass
* Adds subcls as a subclass of supercls.
* Locking: runtimeLock must be held by the caller.
**********************************************************************/
static void addSubclass(Class supercls, Class subcls)
{
if (supercls && subcls) {
...
subcls->data()->nextSiblingClass = supercls->data()->firstSubclass;
supercls->data()->firstSubclass = subcls;
...

addSubclass的方法与root类似,链表的表头为supercls中的firstSubclss.

整体结构如图 省略了尾(空)结点

rootClass

追究细节,先看1处省略的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ro = (const class_ro_t *)cls->data();
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro;
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
rw->ro = ro;
//MARK: - 自注解 每次递归都会把路径上的cls设置一些数据 RW_REALIZED 代表isRealized,所以元类不会死循环
rw->flags = RW_REALIZED|RW_REALIZING;
cls->setData(rw);
}

isMeta = ro->flags & RO_META;

rw->version = isMeta ? 7 : 0; // old runtime went up to 6

ro的声明const class_ro_t *ro;

ro = (const class_ro_t *)cls->data();

当看到data()的实现不免疑惑 返回rw*类型,为什么要强转ro*类型呢? 为什么能转呢?

1
2
3
class_rw_t *data() { 
return bits.data();
}

看下class_ro_t与class_rw_t的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif

const uint8_t * ivarLayout;

const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;

const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;

const class_ro_t *ro;

method_array_t methods;
property_array_t properties;
protocol_array_t protocols;

Class firstSubclass;
Class nextSiblingClass;

char *demangledName;

#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
...

ro 9*8字节,rw 8*8字节

这得从编译时说起

编译时还没有rw,看下ro的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct _class_ro_t {
unsigned int flags;
unsigned int instanceStart;
unsigned int instanceSize;
const unsigned char *ivarLayout;
const char *name;
const struct _method_list_t *baseMethods;
const struct _objc_protocol_list *baseProtocols;
const struct _ivar_list_t *ivars;
const unsigned char *weakIvarLayout;
const struct _prop_list_t *properties;
};

struct _class_t {
struct _class_t *isa;
struct _class_t *superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};

对比运行时objc_class的数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache;
class_data_bits_t bits;
}
typedef uint32_t mask_t;
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
}

// ---------展开后----------
struct objc_class {
isa_t isa;
Class superclass;
struct bucket_t *_buckets;
uint32_t _mask;
uint32_t _occupied;
class_data_bits_t bits;
}

对比数据结构_class_t与objc_class 发现

objc_class的bits对应于_class_t的ro

就此清晰了,编译时确定的ro结构,在运行时初始化类时通过objc_class的data方法获取到有效的地址接下来会赋给新建的rw。

methodizeClass
1
2
3
4
5
6
7
8
/***********************************************************************
* methodizeClass
* Fixes up cls's method list, protocol list, and property list.
* Attaches any outstanding categories.
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static void methodizeClass(Class cls)
{

method_list_t

1
2
3
4
5
6
7
8
9
10
11
12
// Two bits of entsize are used for fixup markers.
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {
bool isFixedUp() const;
void setFixedUp();

uint32_t indexOfMethod(const method_t *meth) const {
uint32_t i =
(uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
assert(i < count);
return i;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/***********************************************************************
* entsize_list_tt<Element, List, FlagMask>
* Generic implementation of an array of non-fragile structs.
*
* Element is the struct type (e.g. method_t)
* List is the specialization of entsize_list_tt (e.g. method_list_t)
* FlagMask is used to stash extra bits in the entsize field
* (e.g. method list fixup markers)
**********************************************************************/
template <typename Element, typename List, uint32_t FlagMask>
struct entsize_list_tt {
uint32_t entsizeAndFlags;
uint32_t count;
Element first;

名词

malloc

c++的atuo 根据表达式推断出数据类型