- 智网科技
- ###
- 分类: 履历之谈
- 阅读量: 51
在 Objective-C 中,NSObject 是绝大少数类的基类。而在 NSObject 中有两个类办法 load 和 initialize,那这两个办法是在什么机遇被挪用呢?父类、Category 的挪用次序又是怎样的呢?上面j9九游会深化 runtime 源码 来一同学习记载下。要是以为两头局部繁琐,可以间接跳到文末检察结论。
+load
j9九游会先来看 load 是什么时分被挪用的,在 load 办法里打断点,看到如下的实行历程
翻开 runtime 的源码,j9九游会看到 load_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
|
###**********************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
* Calls ABI-agnostic code after taking ABI-specific locks.
*
* Locking: write-locks runtimeLock and loadMethodLock
###********************************************************/
__private_extern__ const char *
load_images(enum dyld_image_states state, uint32_t infoCount,
const struct dyld_image_info infoList[])
{
BOOL found;
recursive_mutex_lock(&loadMethodLock);
// Discover load methods
rwlock_write(&runtimeLock);
found = load_images_nolock(state, infoCount, infoList);
rwlock_unlock_write(&runtimeLock);
// Call +load methods (without runtimeLock - re-entrant)
if (found) {
call_load_methods();
}
recursive_mutex_unlock(&loadMethodLock);
return NULL;
}
|
这里j9九游会发明两个紧张的办法 load_images_nolock 和 call_load_methods,接上去j9九游会再一同检察这两个办法的详细完成。
load_images_nolock
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
|
###**********************************************************
* load_images_nolock
* Prepares +load in the given images which are being mapped in by dyld.
* Returns YES if there are now +load methods to be called by call_load_methods.
*
* Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images
###********************************************************/
__private_extern__ BOOL
load_images_nolock(enum dyld_image_states state,uint32_t infoCount,
const struct dyld_image_info infoList[])
{
BOOL found = NO;
uint32_t i;
i = infoCount;
while (i--) {
header_info *hi;
for (hi = FirstHeader; hi != NULL; hi = hi->next) {
const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress;
if (hi->mhdr == mhdr) {
prepare_load_methods(hi);
found = YES;
}
}
}
return found;
}
|
prepare_load_methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
__private_extern__ void prepare_load_methods(header_info *hi)
{
size_t count, i;
rwlock_assert_writing(&runtimeLock);
class_t **classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
class_t *cls = remapClass(classlist[i]);
schedule_class_load(cls);
}
category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
// Do NOT use cat->cls! It may have been remapped.
class_t *cls = remapClass(cat->cls);
realizeClass(cls);
assert(isRealized(cls->isa));
add_category_to_loadable_list((Category)cat);
}
}
|
这里j9九游会发明,class 和 category 被离开处置,先经过 schedule_class_load 将必要实行 load 的 class 添加到一个全局列内外,之后再经过 add_category_to_loadable_list 将必要实行 load 的 category 添加到另一个全局列内外。这两个列表的界说如下,
1
2
3
4
5
|
// List of classes that need +load called (pending superclass +load)
// This list always has superclasses first because of the way it is constructed
static struct loadable_class *loadable_classes NOBSS = NULL;
// List of categories that need +load called (pending parent class +load)
static struct loadable_category *loadable_categories NOBSS = NULL;
|
schedule_class_load
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
###**********************************************************
* prepare_load_methods
* Schedule +load for classes in this image, any un-+load-ed
* superclasses in other images, and any categories in this image.
###********************************************************/
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
static void schedule_class_load(class_t *cls)
{
assert(isRealized(cls)); // _read_images should realize
if (cls->data->flags & RW_LOADED) return;
class_t *supercls = getSuperclass(cls);
if (supercls) schedule_class_load(supercls);
add_class_to_loadable_list((Class)cls);
changeInfo(cls, RW_LOADED, 0);
}
|
这里j9九游会可以看出,class 的处置是递归处置父类,确保父类先被添加到 loadable_classes 中。至此,两个列内外曾经存好了必要实行 load 办法的类和 category。上面再回到 call_load_methods
call_load_methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
__private_extern__ void call_load_methods(void)
{
static BOOL loading = NO;
BOOL more_categories;
recursive_mutex_assert_locked(&loadMethodLock);
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
loading = NO;
}
|
起首从 loadable_classes 中遍历取出类实行 call_class_loads 办法,该办法的详细完成如下
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
|
###**********************************************************
* call_class_loads
* Call all pending class +load methods.
* If new classes become loadable, +load is NOT called for them.
*
* Called only by call_load_methods().
###********************************************************/
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = NULL;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
IMP load_method = classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", _class_getName(cls));
}
(*load_method) ((id) cls, SEL_load);
}
// Destroy the detached list.
if (classes) _free_internal(classes);
}
|
这里j9九游会发明了 load 办法的实质,是间接实行函数指针,因而 load 办法不会实行 objc_msgSend 的那一整套流程,objc_msgSend 的完备流程可以看我写的《深化了解 Objective-C 的办法挪用流程》
call_category_loads 的终极完成也和 call_class_loads 一样都是间接获取函数指针来实行,这里就不贴源码了。
load 办法挪用总结
经过上述源码的剖析,j9九游会晓得了 load 是在被添加到 runtime 时开端实行,父类开始实行,然后是子类,最初是 Category。又由于是间接获取函数指针来实行,不会像 objc_msgSend 一样会无方法查找的历程。
+initialize
理解了 load 办法的实质,initialize 是不是也是经过间接获取函数指针来实行呢?接上去j9九游会再联合 runtime 源码一同验证下。
initialize 实行的要害代码是 _class_initialize,详细完成如下
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
|
###**********************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
*
* Called only from _class_lookupMethodAndLoadCache (or itself).
###********************************************************/
__private_extern__ void _class_initialize(Class cls)
{
Class supercls;
BOOL reallyInitialize = NO;
// Get the real class from the metaclass. The superclass chain
// hangs off the real class only.
cls = _class_getNonMetaClass(cls);
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = _class_getSuperclass(cls);
if (supercls && !_class_isInitialized(supercls)) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
monitor_enter(&classInitLock);
if (!_class_isInitialized(cls) && !_class_isInitializing(cls)) {
_class_setInitializing(cls);
reallyInitialize = YES;
}
monitor_exit(&classInitLock);
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: calling +[%s initialize]",
_class_getName(cls));
}
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
_class_getName(cls));
}
// Done initializing.
......
}
|
经过上述代码,j9九游会发明 initialize 也是取出父类递归实行,确保父类的办法先被实行到。
最要害的是
1
|
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
|
这里j9九游会发明 initialize 终极是经过 objc_msgSend 来实行的,即 initialize 是会颠末一系列办法查找来实行的。
initialize 办法挪用总结
initialize 终极是经过 objc_msgSend 来实行的,objc_msgSend 会实行一系列办法查找,而且 Category 的办法会掩盖类中的办法。objc_msgSend 的办法查找流程可以看我写的《深化了解 Objective-C 的办法挪用流程》
总结
经过阅读 runtime 的源码,j9九游会晓得了 +load 和 +initialize 办法完成的细节,明确了它们的挪用机制和各自的特点。上面j9九游会绘制一张表格,以愈加直观的方法来牢固j9九游会对它们的了解:
+load | +initialize | |
挪用机遇 | 被添加到 runtime 时 | 收到第一条音讯前,大概永久不挪用 |
挪用次序 | 父类->子类->分类 | 父类->子类 |
挪用次数 | 1次 | 屡次 |
能否必要显式挪用父类完成 | 否 | 否 |
能否相沿父类的完成 | 否 | 是 |
分类中的完成 | 类和分类都实行 | 掩盖类中的办法,只实行分类的完成 |
这是我写的 runtime 系列文章中的一篇,另有以下几篇从其他方面临 runtime 举行了介绍
参考材料: