load方法
测试代码:新建Person类,在里面添加断点:
#import "Person.h" @implementation Person + (void)load { NSLog(@"load"); } @end
调用顺序如图:
接下来通过源码分析一下底层调用;
load_images函数
首先看断点3处的load_images中函数:
void load_images(const char *path __unused, const struct mach_header *mh) { if (!didInitialAttachCategories && didCallDyldNotifyRegister) { didInitialAttachCategories = true; loadAllCategories(); } // 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函数,准备需要调用的所有load函数方法,接下来看一下prepare_load_methods函数;prepare_load_methods函数
void prepare_load_methods(const headerType *mhdr) { size_t count, i; runtimeLock.assertLocked(); classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count); for (i = 0; i < count; i++) { schedule_class_load(remapClass(classlist[i])); } category_t * const *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, nil); ASSERT(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); } }
可以看到schedule_class_load函数内部首先被调用了,接下来调用了add_category_to_loadable_list,将分类的load插入表中;
schedule_class_load函数
接下来查看schedule_class_load的内部实现,可以发现通过递归先查找父类里面的load,通过add_class_to_loadable_list函数插入表中,再查找当前类的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->getSuperclass()); add_class_to_loadable_list(cls); cls->setInfo(RW_LOADED); }
loadable_classes是一个全局的结构体, add_class_to_loadable_list作用是将模块里所有类的load函数存放到loadable_classes中,并且放到末尾;
add_class_to_loadable_list函数接下来看add_class_to_loadable_list函数:
void add_class_to_loadable_list(Class cls) { ... 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)); } loadable_classes[loadable_classes_used].cls = cls; loadable_classes[loadable_classes_used].method = method; loadable_classes_used++; }
add_category_to_loadable_list函数add_category_to_loadable_list同理,将所有分类的load放到分类表中,先编译的会先加入,后编译的后加入:
void add_category_to_loadable_list(Category cat) { ... if (loadable_categories_used == loadable_categories_allocated) { loadable_categories_allocated = loadable_categories_allocated*2 + 16; loadable_categories = (struct loadable_category *) realloc(loadable_categories, loadable_categories_allocated * sizeof(struct loadable_category)); } loadable_categories[loadable_categories_used].cat = cat; loadable_categories[loadable_categories_used].method = method; loadable_categories_used++; }
call_load_methods函数
接下来调用call_load_methods,内部通过do-while循环可以看到优先通过call_class_loads函数调用类的load函数,接着通过call_category_loads函数调用分类的函数,故类的load会先与分类的load被调用:
void call_load_methods(void) { ... 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); ... }
call_class_loads函数
从表中的索引0开始依次取出load函数进行调用, 先编译的类会优先被调用,内部通过(*load_method)(cls, @selector(load))函数调用load函数,是通过C语言函数调用的流程,不是OC的给对象发送消息,所以不会走消息发送流程,也就是说如果一个类实现了load函数就会调用,如果没有实现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, @selector(load)); } // Destroy the detached list. if (classes) free(classes); }
分类调用探究
为Person增加分类,新建Person+Cate文件
@implementation Person (Cate) + (void)load { NSLog(@"load"); }
@end
通过断点调试,调用顺序如图:
call_category_loads函数
前面的与上面一致,只看call_category_loads调用分类的函数,从表中的索引0开始依次取出load函数进行调用, 先编译进入的分类会优先被调用:
static bool call_category_loads(void) { ... for (i = 0; i < used; i++) { Category cat = cats[i].cat; load_method_t load_method = (load_method_t)cats[i].method; Class cls; if (!cat) continue; cls = _category_getClass(cat); if (cls && cls->isLoadable()) { if (PrintLoading) { _objc_inform("LOAD: +[%s(%s) load]\n", cls->nameForLogging(), _category_getName(cat)); } (*load_method)(cls, @selector(load)); cats[i].cat = nil; } } ... }
initialize方法
当类第一次接收到消息的时候initialize会被调用;
测试代码如下:
@implementation Person + (void)initialize { NSLog(@"Person - initialize"); }
@end
首次调用:
Person *p = [Person new]
__objc_msgSend_uncached函数
首先进入__objc_msgSend_uncached,接下来调用lookUpImpOrForward:
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) { ... cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE); ... }
realizeAndInitializeIfNeeded_locked函数
接下来调用realizeAndInitializeIfNeeded_locked:
static Class realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize) { ... if (slowpath(initialize && !cls->isInitialized())) { cls = initializeAndLeaveLocked(cls, inst, runtimeLock); } return cls; }
initializeAndLeaveLocked函数
接下来调用initializeAndLeaveLocked函数:
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock) { return initializeAndMaybeRelock(cls, obj, lock, true); }
initializeAndMaybeRelock函数
接下来调用initializeAndMaybeRelock函数:
initializeNonMetaClass函数
static Class initializeAndMaybeRelock(Class cls, id inst, mutex_t& lock, bool leaveLocked) { ... initializeNonMetaClass(nonmeta); }
接下来调用initializeNonMetaClass,可以看出如果存在父类,并且父类如果没有调用,则优先调用父类,如果已经调用initialize,下次父类不会再调用initialize:
void initializeNonMetaClass(Class cls) { supercls = cls->getSuperclass(); if (supercls && !supercls->isInitialized()) { initializeNonMetaClass(supercls); } ... { callInitialize(cls); } }
callInitialize函数
接下来调用callInitialize:
void callInitialize(Class cls) { ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize)); asm(""); }
可以看到callInitialize中initialize函数的调用是通过objc_msgSend消息发送机制,由OC的消息机制可知,如果子类没有实现initialize,父类实现了,则父类的可能会被调用多次initialize函数;
总结
load方法的调用时机?
- load方法会在runtime加载类、分类时调用,main函数之前(通过C函数调用);
- 每个类、分类的load,在程序运行过程中只调用一次;
- 父类 > 子类 > 分类
同一级别按照编译先后顺序调用(先编译,先调用)
initialize方法的调用时机?
- initialize方法会在类第一次接收到消息时调用(通过objc_msgSend调用);
- 先调用父类的initialize,再调用子类的initialize(先初始化父类,再初始化子类,每个类只会初始化1次);
- 如果子类没有实现initialize,会调用父类的initialize,所以父类的+initialize可能会被调用多次;
- 如果分类实现了initialize,会优先调用分类的initialize(分类的方法追加到了方法列表的前面,所以会优先调用);
本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!