1、类和分类的+load方法调用顺序

分析源码:
_objc_init(运行时初始化方法)

load_images(加载模块方法)

call_load_methods(调用load方法)

call_class_loads(调用类的load方法)

call_category_loads(调用分类的load方法)

  1. static void call_class_loads(void)
  2. {
  3. // 读取所有类
  4. struct loadable_class *classes = loadable_classes;
  5. ......
  6. // 遍历所有类
  7. for (i = 0; i < used; i++) {
  8. // 找到class对象中的+load方法指针
  9. load_method_t load_method = (load_method_t)classes[i].method;
  10. // 调用+load方法
  11. (*load_method)(cls, @selector(load));
  12. }
  13. ......
  14. }
  1. static bool call_category_loads(void)
  2. {
  3. // 读取所有分类
  4. struct loadable_category *cats = loadable_categories;
  5. ......
  6. // 遍历所有分类
  7. for (i = 0; i < used; i++) {
  8. // 找到分类里+load方法指针
  9. load_method_t load_method = (load_method_t)cats[i].method;
  10. // 调用+load方法
  11. (*load_method)(cls, @selector(load));
  12. }
  13. ......
  14. }

由源码可知,先调用了call_class_loads方法再调用了call_category_loads方法,所以类和分类调用顺序是:
先调用所有类中的+load方法,再调用所有分类的+load方法。

2、不同类间的+load方法调用顺序

分析运行时源码:
prepare_load_methods(预加载+load方法)

schedule_class_load(定制类的load方法)

  1. static void schedule_class_load(Class cls)
  2. {
  3. if (!cls) return;
  4. ASSERT(cls->isRealized()); // _read_images should realize
  5. if (cls->data()->flags & RW_LOADED) return;
  6. // Ensure superclass-first ordering
  7. schedule_class_load(cls->getSuperclass());
  8. add_class_to_loadable_list(cls);
  9. cls->setInfo(RW_LOADED);
  10. }

通过分析源码可知:schedule_class_load方法内部有一个递归调用,每次传入参数是cls的superclass,将cls添加到loadable_classes数组中,所以最终结果导致在loadable_classes数组中父类class会排在子类class前面,再结合call_class_loads方法得出:
1、先调用父类中的+load方法,再调用子类中的+load方法。
2、没有继承关系的类,按照编译顺序调用+load方法。

3、不同分类间的+load方法调用顺序

分析源码:

  1. void add_category_to_loadable_list(Category cat)
  2. {
  3. ......
  4. loadable_categories[loadable_categories_used].cat = cat;
  5. ......
  6. }

结合call_category_loads方法得出:
分类是按照编译顺序调用+load方法

4、总结

4.1、+load方法特点

+load方法会在runtime加载类、分类时调用
每个类、分类的+load,在程序运行过程中只调用一次
+load是通过方法指针调用,所以分类+load方法不会覆盖类的+load方法

4.2、调用顺序

先调用类的+load
按照编译先后顺序调用(先编译,先调用)
调用子类的+load之前会先调用父类的+load

再调用分类的+load
按照编译先后顺序调用(先编译,先调用)