+load 方法会在runtime加载类、分类时调用
+load 在runtime加载时,是直接使用函数指针调用,所以不会走消息转发流程
每个类、分类的+load,在程序运行过程中只调用一次
+load 调用顺序
下图,帮我们验证了上述说明的正确性。在Person最后编译,但是他的+load方法在第一个执行;Test01和Test02的+load 方法会按照文件编译顺序执行(先编译先执行,后编译后执行)。
苹果源码是在不停更新的,不同版本的源码实现不同,我这里使用的版本是"818.2"
✨ 这里推荐一个可以被编译的源码https://github.com/LGCooci/KCCbjc4_debug
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);
/// 预处理category和类的load方法
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
从这里可以看出,在源码中处理load方法,分类两步:
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++) {
// 收集当前类和父类的,父类优先
// 将类和load方法存入add_class_to_loadable_list数组中
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());
/// 将分类的类和load方法存入loadable_categories数组中
add_category_to_loadable_list(cat);
}
}
从prepare_load_methods函数中看出: