load方法是在runtime时候调用的.
//objc-os.mm
_objc_init()
load_images()
prepare_load_methods()
call_load_methods()
call_load_methods实现如下
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);
可见是所有的类的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;
(*load_method)(cls, SEL_load);
}
...
}
可以看到是通过for遍历调用的,那么loadable_classes数组的顺序是怎样的,那么其调用顺序就是怎样的
根据上面的调用顺序call_load_methods前面有一个prepare_load_methods,
实现如下
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
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);
}
}
_getObjc2NonlazyClassList获取的类的列表是和编译顺序是一样的.那么看schedule_class_load内部实现
static void schedule_class_load(Class cls)
{
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
...
}
递归调用获取了父类
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++;
}
我们上面说了loadable_classes是怎么生成的,决定了类的load方法的调用顺序.
所以先传入schedule_class_load也会先被add_class_to_loadable_list添加,而添加一个类的时候,由于schedule_class_load先递归调用父类的,所以父类的又先于当前类被添加
而传入schedule_class_load的类是遍历从_getObjc2NonlazyClassList中获取的.而该方法取决于编译顺序,
所以:
- 同层级的类,先编译的先调用
- 父类先于子类调用
比如有Person,Student,Dog类,Student继承自Person,Peron和Dog是同级别,都继承自NSObject
-
Person的调用肯定在Student前面 -
Dog的调用顺序取决于其编译顺序
如果编译顺序是,Dog在最前面
Dog->Student->Person
Dog->Person->Student
那么调用顺序是
Dog->Person->Student
如果编译顺序是,Dog在最后面
Person->Student->Dog
Student->Person->Dog
那么调用顺序是
Person->Student->Dog
如果编译顺序是,Dog在中间
Person->Dog->Student or
Student->Dog->Person
那么调用顺序是
Person->Dog->Student
Person->Student->Dog
给Person增加分类test1和test2,无疑,分类肯定是最后调用
2020-09-26 15:38:30.528745+0800 objc-test[5734:503102] Person load
2020-09-26 15:38:30.529252+0800 objc-test[5734:503102] Student load
2020-09-26 15:38:30.529321+0800 objc-test[5734:503102] Dog load
2020-09-26 15:38:30.529372+0800 objc-test[5734:503102] Person Test1
2020-09-26 15:38:30.529418+0800 objc-test[5734:503102] Person Test2
那么分类的调用顺序是怎样的呢?
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;
// Detach current loadable list.
struct loadable_category *cats = loadable_categories;
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = nil;
loadable_categories_allocated = 0;
loadable_categories_used = 0;
// Call all +loads for the detached list.
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()) {
(*load_method)(cls, SEL_load);
}
}
...
}
可以看到也是遍历调用,那么就取决于loadable_categories的顺序,回到prepare_load_methods
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);
}
_getObjc2NonlazyCategoryList取决于编译顺序
在build phases中更换test1和test2的编译顺序,load调用顺序随之改变
给Dog也增加分类,test1和test2,打印顺序仍旧同编译顺序一致