从objc苹果的源码中可以看到如下代码:
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); // 准备调用类以及父类中的load方法 prepare_load_methods((const headerType *)mh); } // Call +load methods (without runtimeLock - re-entrant) // 真正调用load方法 call_load_methods(); }
在runtime加载类和分类的load方法时候,会调用load_image方法,在load_images中会调用prepare_load_methods和call_load_methods,prepare_load_methods源码如下:
void prepare_load_methods(const headerType *mhdr) { size_t count, i; runtimeLock.assertLocked(); // 获取实现load方法的类的数组 classref_t const *classlist = _getObjc2NonlazyClassList(mhdr, &count); // 遍历load方法类的数组 for (i = 0; i < count; i++) { // 存储当前类的父类和当前类load方法 schedule_class_load(remapClass(classlist[i])); } // 获取实现了load方法分类的数组 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); } }
在prepare_load_methods方法中,会通过_getObjc2NonlazyClassList获得实现load方法类的数组,然后遍历这个数组通过schedule_class_load获得当前类的父类和当前类的load方法,然后通过_getObjc2NonlazyCategoryList方法获得实现了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会被递归调用,判断该类未被初始化之后,优先调用父类,然后再保存当前类相关信息,并将该类设置为已经初始化
add_class_to_loadable_list源码如下:
void add_class_to_loadable_list(Class cls) { IMP method; loadMethodLock.assertLocked(); // 获取当前类load方法的地址 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)); } // 把clshe和method保存到loadable_classes的结构体数组中 loadable_classes[loadable_classes_used].cls = cls; loadable_classes[loadable_classes_used].method = method; loadable_classes_used++; }
把数据保存到loadable_classes中,并用loadable_classes_used计数,在准备方法调用完成之后,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; }
会通过do-while的遍历,让之前保存的loadable_classes_usd>0或者more_categories为真,会通过while遍历类的load方法,再调用分类的load
call_class_loads源码如下:
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] ", cls->nameForLogging()); } (*load_method)(cls, @selector(load)); } // Destroy the detached list. if (classes) free(classes); }
load方法的调用是通过地址直接调用的
总结:
load方法会在runtime加载类、分类时调用,每个类、分类的+load,在程序运行过程中只调用一次
调用顺序:
先调用类的load,按照编译先后顺序调用,调用子类的+load之前会先调用父类的load
再调用分类的load,也是按照先编译、先调用的规则