load 和 initialize方法调用
2020-02-28 本文已影响0人
只写Bug程序猿
我们前边说,程序启动肯定会来到
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);
}
接下来研究一下load_images
load_images
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
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
这里重点代码在prepare_load_methods
和call_load_methods
1. prepare_load_methods
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
//获取所有非懒加载类,这里实现了load方法所以加载懒加载类
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
if (cls->isSwiftStable()) {
_objc_fatal("Swift class extensions and categories on Swift "
"classes are not allowed to have +load methods");
}
realizeClassWithoutSwift(cls);
assert(cls->ISA()->isRealized());
//添加分类到非懒加载list中
add_category_to_loadable_list(cat);
}
}
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);
}
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
//拿到load方法method
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
//判断是否需要扩容
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
//将cls给数组的属性 给model赋值
loadable_classes[loadable_classes_used].cls = cls;
//将method给数组的method
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
struct loadable_class {
Class cls; // may be nil
IMP method;
};
- 拿到非懒加载类
classlist
,然后循环遍历调用schedule_class_load
-
schedule_class_load
,递归调用找到自己和父类的load方法然后add_class_to_loadable_list
加到数组中去 - 然后判断是否需要扩容,这里说下为什么 + 16. loadable_class是一个结构体,cls8字节,imp8字节所以加16
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;
}
- 先放入自动释放池然后调用
- 循环调用load方法
- 最后出站,自动释放,保证只调用一次
- 从代码的先后顺序可以看出来,load方法是先调用主类,然后调用分类
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 = nil;
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;
//强转
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
//直接调用函数
(*load_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}
这里是具体的调用方法
- 循环遍历拿到非懒加载类
- 得到
imp
并且强转成load_method_t
,然后直接调用函数
call_category_loads和call_class_loads相似
initialize
initialize方法调用时机参照类和分类的加载
总结:
- load方法是在load_images里调用的
- load方法调用,主类优先级优于分类,也就是说先调用主类的load然后执行分类的load方法
- 普通方法调用和load方法相反,如果主类和分类都有同名的方法,则先调用分类的方法.因为attachmethodlist是先加载类的方法,然后将分类的方法插入到了前边,所以先调用分类方法,这里会造成一个分类方法覆盖主类方法的假象,其实是没有覆盖的,只不过是在方法查找的时候先找到分类然后查找结束.