iOS中 性能优化之浅谈load与initialize
Posted 韩俊强
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS中 性能优化之浅谈load与initialize相关的知识,希望对你有一定的参考价值。
一. +load
源码分析
extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
rwlock_writer_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
在runtime源码中,我们可以看到,+load
方法是在load_images
中通过call_load_methods
调用的。
更具体的来说是在运行时加载镜像时,通过prepare_load_methods
方法将+load方法准备就绪,而后执行call_load_methods
,调用+load
方法。
1. prepare_load_methods
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertWriting();
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);//获取所有类列表
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
在prepare_load_methods
方法中,分为两个步骤:
一是,获取了所有类后,遍历列表,将其中有+load
方法的类加入loadable_class
;
二是,获取所有的类别,遍历列表,将其中有+load
方法的类加入loadable_categories
.
另外值得注意的一点是schedule_class_load
方法的实现:
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
在该方法中会首先通过schedule_class_load(cls->superclass)
确保父类中的 +load
方法被加入loadable_class
(如果父类有+load
方法的话),从而保证父类的+load
方法总是在子类之前调用。
也因此,在覆写+load
方法时,不需要调用super方法。
2. call_load_methods
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
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);
objc_autoreleasePoolPop(pool);
loading = NO;
}
call_load_methods
方法注释写得非常明了,首先调用类的load方法,在call_class_loads
方法中通过在第一步读取prepare_load_methods
步骤里的loadable_classes
,遍历列表并调用+load
方法,然后类似的调用类别的+load
方法,第三步算是处女座的处理,处理异常。
3. call_class_loads
static void call_class_loads(void)
{
int i;
//1.获取列表
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
//2.循环调用load方法
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
(*load_method)(cls, SEL_load);
}
// 3. 释放列表
if (classes) free(classes);
}
这里我们需要特别注意的是第二部分中:
(*load_method)(cls, SEL_load);
这段代码也就是说+load
方法的调用是通过直接使用函数内存地址的方式实现的,而不是更常见的objc_msgSend
来发送消息.
也正是这句代码造就了+load
方法的最大特点:类,父类与分类之间+load
方法的调用是互不影响的.也就是,子类不会主动调用父类的+load
方法,如果类与分类都实现了+load',那么两个
+load`方法都会被调用.