我们都知道initialize
方法只有当类或子类第一次收到消息时才会调用。
我们在这个方法里打上断点来看看,比如我调用[Human new]
, 可以看到如下堆栈信息。
从源码里我们可以看到,在
lookUpImpOrForward
里面,有初始化的判断。
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock may have been dropped but is now locked again
// If sel == initialize, class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
一步步往下看 ,发现在 initializeNonMetaClass
里 调用了callInitialize(cls);
,而callInitialize
实现如下
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
可以看出在这里通过objc_msgSend
的方式 对initialize
进行了调用。
再来看看子类和父类同时实现initialize
的情况
我这里Person
类继承Human
,都实现initialize
,调用一些[Person new]
,按理来说应该只调用Person
的initialize
。
实际结果如下:
发现先调用了父类的initialize
,再调用了子类的initialize
。
在initializeNonMetaClass
源码里我们可以找到原因所在
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
这里会确保在开始初始化cls
之前,super
已完成初始化。所以这里会调用父类的initialize
,往后面走再调用子类的initialize
。
如果在分类里实现initialize
,那么分类的initialize
则会‘‘覆盖’’本类的initialize
,这种情况我们在这一篇中已经捋过了(以及load方法)。
那么最后总结一下+load
和+initialize
区别
+load | +initialize | |
---|---|---|
调用时机 | 在类和分类加入Runtime的时候调用 | 类或子类第一次收到消息时 |
调用方式 | 直接通过函数指针调用((*load_method)(cls, @selector(load))) | objc_msgSend |
调用顺序 | 父类->子类->分类(分类顺序看文件编译顺序) | 父类->子类(分类有实现,则父类->分类) |
本类未实现,是否调用父类 | 否 | 是 |
存在分类时 | 全都调用,晚于本类的调用 | ‘‘覆盖’’本类的方法,只调用分类的 |