1、+initialize方法调用时机

查看objc_msgSend源码,objc_msgSend是汇编语言理解起来困难,所以可以从消息发送的过程入手:
isa->找到类/元类对象->查找方法->调用
从查找方法部分入手
class_getInstanceMethod(查找对象方法)/class_getClassMethod(查找类方法)

lookUpImpOrForward(查找方法实现)

  1. IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
  2. {
  3. ......
  4. // 如果没有初始化,需要调用initialize方法
  5. if ((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized()) {
  6. initializeNonMetaClass (_class_getNonMetaClass(cls, inst));
  7. }
  8. ......
  9. }


initializeNonMetaClass

  1. void initializeNonMetaClass(Class cls)
  2. {
  3. ......
  4. // 拿到父类
  5. supercls = cls->getSuperclass();
  6. // 父类如果没有初始化,先初始化父类
  7. if (supercls && !supercls->isInitialized()) {
  8. initializeNonMetaClass(supercls);
  9. }
  10. ......
  11. callInitialize(cls);
  12. ......
  13. }
  14. // 调用cls的initialize方法
  15. void callInitialize(Class cls)
  16. {
  17. ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
  18. asm("");
  19. }

通过查看源码可知,在objc_msgSend(消息发送)过程中,会先调用类的+initialize方法。
观察到initializeNonMetaClass方法内部有递归调用,递归参数是superclass,所以调用子类+initialize前,会先判断父类是否已经初始化了,如果没有会先调用父类的+initialize方法。
总结:
1、+initialize方法会在类第一次接收到消息时调用。
2、调用顺序
先调用父类的+initialize,再调用子类的+initialize(先初始化父类,再初始化子类,每个类只会初始化1次)

2、+initialize用途

initialize用途就是在第一次使用这个类的时候做一些事情。

3、+initialize和+load对比

3.1、调用时机

+load在runtime加载类、分类时调用(只会调用一次)
+initialize是类第一次接收到消息时调用,每一个类只会initialize一次(如果子类没有实现initialize方法,父类的initialize方法可能会被调用多次)

3.2、调用顺序

3.21、+load

1、先调用类的+load
先编译的类,优先调用+load
调用子类+load之前,会先调用父类的+load

2、再调用分类
先编译的分类,优先调用+load

3.2.2、+initialize

1、先初始化父类
2、再初始化子类(可能最终调用的是父类的+initialize方法)

3.1、调用方式

+load是通过方法指针调用,+initialize是通过objc_msgSend方式调用,所以+initialize会有以下特点:
1、如果子类没有实现+initialize方法,会调用父类的+initialize(所以父类的+initialize可能会被调用多次,第一次为自己初始化时调用,其他为子类消息发送调用)
2、如果分类实现了+initialize,会覆盖主类的+initialize