1、+initialize方法调用时机
查看objc_msgSend源码,objc_msgSend是汇编语言理解起来困难,所以可以从消息发送的过程入手:
isa->找到类/元类对象->查找方法->调用
从查找方法部分入手
class_getInstanceMethod(查找对象方法)/class_getClassMethod(查找类方法)
↓
lookUpImpOrForward(查找方法实现)
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
......
// 如果没有初始化,需要调用initialize方法
if ((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized()) {
initializeNonMetaClass (_class_getNonMetaClass(cls, inst));
}
......
}
↓
initializeNonMetaClass
void initializeNonMetaClass(Class cls)
{
......
// 拿到父类
supercls = cls->getSuperclass();
// 父类如果没有初始化,先初始化父类
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
......
callInitialize(cls);
......
}
// 调用cls的initialize方法
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
通过查看源码可知,在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
3.2.2、+initialize
1、先初始化父类
2、再初始化子类(可能最终调用的是父类的+initialize方法)
3.1、调用方式
+load是通过方法指针调用,+initialize是通过objc_msgSend方式调用,所以+initialize会有以下特点:
1、如果子类没有实现+initialize方法,会调用父类的+initialize(所以父类的+initialize可能会被调用多次,第一次为自己初始化时调用,其他为子类消息发送调用)
2、如果分类实现了+initialize,会覆盖主类的+initialize