realizeClassWithoutSwift
realizeClassWithoutSwift是初始化类的核心方法,主要做了如下事情:
- 读取data数据,设置ro、rw
- 归调用realizeClassWithoutSwift完善类的继承链关系
- 调用methodizeClass,完善类信息(方法、分类的方法、属性列表、协议列表)
读取 data 数据,并设置 ro 、rw - ro 表示 read only,其在编译期的时候就已经确定了内存,包含了类的名称、方法列表、协议列表、属性列表和成员变量列表的信息。由于它是只读的,确定了之后就不会发生变化,所以属于 干净的内存(Clean Memory) - rw 表示 read Write,由于 OC 的动态性,所以可能会在运行时动态往类中添加属性、方法和协议 - rwe 表示 read Write ext,是对rw做了进一步的改进。 rw 中只有 10% 左右的类真正更改了它们的方法、属性等,所以新增加了 rwe,即是类的额外信息,rw 和 rwe 都属于 脏内存(dirty memory)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
auto ro = (const class_ro_t *)cls->data();
// 判断是否是元类
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
// Normal class. Allocate writeable class data.
// 创建 rw
rw = objc::zalloc<class_rw_t>();
// ro 赋值到 rw
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
// 将 rw 赋值给 cls 的 data
cls->setData(rw);
}
递归调用 realizeClassWithoutSwift 完成类的继承链关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 递归调用 `realizeClassWithoutSwift`,设置父类和元类的 `data` 信息加载到内存当中。
// 当 isa 找到根元类之后,因为根元类的 isa 是指向自己的,不会返回 nil 从而导致无限递归,在 remapClass 中对类在表中进行查找的操作,如果表中已有该类,则返回一个空值;如果没有则返回当前类,这样保证了类只加载一次并结束递归
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
// Update superclass and metaclass in case of remapping
// 分别将父类和元类赋值给 `class` 的 `superclass` 和 `classIsa`。
cls->superclass = supercls;
cls->initClassIsa(metacls);
// Connect this class to its superclass's subclass lists
// 注意:class 是双向链接结构,即父类可以找到子类,子类可以找到父类
if (supercls) {
// 将 cls 作为 supercls 的子类添加。
addSubclass(supercls, cls);
} else {
// 将 cls 添加为新的已实现的根类。
addRootClass(cls);
}
调用 methodizeClass 方法,读取方法列表(包括分类)、协议列表、属性列表,然后赋值给 rw,最后返回 cls
1
2
3
4
// Attach categories - 附加分类
methodizeClass(cls, previously);
return cls;
methodizeClass
methodizeClass主要做了两件事:
- 将方法列表、属性列表和协议列表赋值给 rwe。
- 附加分类方法。
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
/***********************************************************************
* 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, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Install methods and properties that the class implements itself.
// 将方法列表、属性列表和协议列表赋值到 rwe
// 获取 ro 的 方法列表
method_list_t *list = ro->baseMethods();
if (list) {
// 对方法列表进行排序操作
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
// 把方法赋值给 rwe 的 method
if (rwe) rwe->methods.attachLists(&list, 1);
}
// 获取 ro 的 属性列表
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
// 把属性赋值给 rwe 的 properties
rwe->properties.attachLists(&proplist, 1);
}
// 获取 ro 的 协议列表
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
// 把协议赋值给 rwe 的 protocols
rwe->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
// 附加分类方法
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}
attachToClass
把分类的数据加入到主类数据中
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
void attachToClass(Class cls, Class previously, int flags)
{
runtimeLock.assertLocked();
ASSERT((flags & ATTACH_CLASS) ||
(flags & ATTACH_METACLASS) ||
(flags & ATTACH_CLASS_AND_METACLASS));
auto &map = get();
// 找到一个分类就进来一次,防止混乱
auto it = map.find(previously);
// 注意:当主类没有实现 load 方法的时候,分类实现了 load 方法,那么会迫使主类进行加载,所以会进入 if 判断里面
if (it != map.end()) {
category_list &list = it->second;
// 判断是否元类
if (flags & ATTACH_CLASS_AND_METACLASS) {
int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
// 附加实例方法
attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
// 附加类方法
attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
} else {
// 附加实例方法
attachCategories(cls, list.array(), list.count(), flags);
}
map.erase(it);
}
}
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order,
// oldest categories first.
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
int flags)
{
if (slowpath(PrintReplacedMethods)) {
printReplacements(cls, cats_list, cats_count);
}
if (slowpath(PrintConnecting)) {
_objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
}
/*
* Only a few classes have more than 64 categories during launch.
* This uses a little stack, and avoids malloc.
*
* Categories must be added in the proper order, which is back
* to front. To do that with the chunking, we iterate cats_list
* from front to back, build up the local buffers backwards,
* and call attachLists on the chunks. attachLists prepends the
* lists, so the final result is in the expected order.
*/
constexpr uint32_t ATTACH_BUFSIZ = 64;
method_list_t *mlists[ATTACH_BUFSIZ];
property_list_t *proplists[ATTACH_BUFSIZ];
protocol_list_t *protolists[ATTACH_BUFSIZ];
uint32_t mcount = 0;
uint32_t propcount = 0;
uint32_t protocount = 0;
bool fromBundle = NO;
bool isMeta = (flags & ATTACH_METACLASS);
// 初始化 rwe,因为要往主类添加方法、属性和协议
auto rwe = cls->data()->extAllocIfNeeded();
// mlists -> 二维数组
for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
// 当前 mcount = 0,ATTACH_BUFSIZ= 64,所以不会进入 if 里面
if (mcount == ATTACH_BUFSIZ) {
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
rwe->methods.attachLists(mlists, mcount);
mcount = 0;
}
// 倒序插入
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
if (propcount == ATTACH_BUFSIZ) {
rwe->properties.attachLists(proplists, propcount);
propcount = 0;
}
proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
}
protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
if (protolist) {
if (protocount == ATTACH_BUFSIZ) {
rwe->protocols.attachLists(protolists, protocount);
protocount = 0;
}
protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
}
}
if (mcount > 0) {
// 排序
prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
// 进行内存平移操作
rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
if (flags & ATTACH_EXISTING) flushCaches(cls);
}
rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
attachLists
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
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
// 获取数组中旧 lists 的大小
uint32_t oldCount = array()->count;
// 计算新的容量大小 = 旧数据大小 + 新数据大小
uint32_t newCount = oldCount + addedCount;
// 根据新的容量大小,开辟一个数组,类型是 array_t,通过 array() 获取
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
// 设置数组的大小
array()->count = newCount;
// 旧的数据从 addedCount 数组下标开始存放旧的 lists,大小 = 旧数据大小 * 单个旧 list 大小
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// 新数据从数组 首位置开始存储,存放新的lists,大小 = 新数据大小 * 单个list大小
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
// 将 list 加入 mlists 的第一个元素,此时的 list 是一个一维数组
list = addedLists[0];
}
else {
// 新的 list 就是分类,来自LRU的算法思维,即最近最少使用算法
// 获取旧的list
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
// 计算容量和 = 旧list个数 + 新lists的个数
uint32_t newCount = oldCount + addedCount;
// 开辟一个容量和大小的集合,类型是 array_t,即创建一个数组,放到 array 中,通过 array() 获取
setArray((array_t *)malloc(array_t::byteSize(newCount)));
// 设置数组的大小
array()->count = newCount;
// 判断old是否存在,如果存在,就将旧的 list 放入到数组的末尾
if (oldList) array()->lists[addedCount] = oldList;
// memcpy(起始位置,放什么,放多大) 内存平移,从数组起始位置存入新的list
//其中 array()->lists 表示首位元素位置
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
从上可知,插入表可分为以下三种情况:
- 多对多: 如果当前调用 attachLists 的 list_array_tt 二维数组中有多个一维数组。 1.1 先计算数组中旧 list 的大小 1.2 计算新的容量大小 = 旧数据大小 + 新数据大小 1.3 根据新的容量大小,开辟一个数组,类型是 array_t,通过 array() 获取 1.4 设置数组大小 1.5 旧的数据从 addedCount 数组下标开始 存放旧的 lists,大小 = 旧数据大小 * 单个旧list大小,即指针偏移 1.6 新数据从数组首位置开始存储,存放新的 lists,大小 = 新数据大小 * 单个list大小,可以理解越晚加进来的越在前面,越在前面,调用时则优先调用
- 零对一: 如果当前调用 attachLists 的 list_array_tt 二维数组为空且新增大小为1。 2.1 直接赋值给 addedList 的第一个 list
- 一对多: 如果当前调用 attachLists 的 list_array_tt 二维数组只有一个一维数组。 3.1 获取旧的list 3.2 计算容量和 = 旧list个数 + 新lists的个数 3.3 开辟一个容量和大小的集合,类型是 array_t,即创建一个数组,放到array 中,通过 array() 获取 3.4 设置数组的大小 3.5 判断 old 是否存在,如果 old 是存在的,那么就将旧的list放入到数组的末尾 3.6 memcpy(起始位置,放什么,放多大) 内存平移,从数组起始位置存入新的list
category
可以看到,category 也是一个结构体,主要包含了category名称,类对象指针,实例方法列表,类方法列表,协议列表,属性列表等信息。但是无法添加实例变量(可以通过关联对象的方式来实现)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct category_t {
const char *name;
classref_t cls;
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods;
WrappedPtr<method_list_t, PtrauthStrip> classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else return protocols;
}
};
category的加载时机
分类数据的加载时机: 根据 类和分类是否实现 load 方法 来区分不同的时机。
- 在 attachCategories 方法中准备分类数据
- 在 attachLists 中将分类数据添加到主类
类和分类是否实现 load
方法可以分为四种情况:
类和分类 | 分类实现 load | 分类未实现 load |
---|---|---|
类实现 load | 非懒加载类 + 非懒加载分类 | 非懒加载类 + 懒加载分类 |
类未实现 load | 懒加载类 + 非懒加载分类 | 懒加载类 + 懒加载分类 |
不同情况的加载时机:
- 非懒加载类 + 非懒加载分类,其数据的加载在 load_images 方法中,首先对类进行加载,然后把分类的信息贴到类中。
- 非懒加载类 + 懒加载分类,其数据加载在 read_image 就加载数据,数据来自data,data在编译时期就已经完成,即data中除了类的数据,还有分类的数据,与类绑定在一起。
- 懒加载类 + 懒加载分类,其数据加载推迟到 第一次消息时,数据同样来自data,data在编译时期就已经完成。
- 懒加载类 + 非懒加载分类,只要分类实现了load,会迫使主类提前加载,即在 _read_images 中不会对类做实现操作,需要在 load_images 方法中触发类的数据加载,即rwe初始化,同时加载分类数据。