ObjC中+initialize方法的规则:
- 惰性调用
- 只调用一次
- 执行时运行时系统处于正常状态
- 会先运行超类的+initialize方法
- 子类没有实现+initialize方法,会执行超类的+initialize方法
- 不要两个class的+initialize方法中互相使用对方,否则会造成初始化不完整
从ObjC源码分析这些规则,首先在类EOCBaseClass中定义+initialize方法:
#import "EOCBaseClass.h"
@implementation EOCBaseClass
+ (void)initialize {
NSLog(@"%@ initialize", self);
}
@end
然后在main方法中创建EOCBaseClass对象
#import <Foundation/Foundation.h>
#import "EOCSubClass.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
EOCSubClass *sub = [[EOCSubClass alloc] init];
}
return 0;
}
然后在+initialize方法处打断点,运行程序:
![](https://img.haomeiwen.com/i1802898/78d3e469d58bd0bf.png)
从调用栈来看,最开始是main()函数,在main()函数运行之前,动态库都已经完成了加在和初始化,class的+load方法均已调用完成。
而且,在调用栈4::lookUpImpOrForward方法中会对接收消息涉及到的class执行realizeClass(cls)进行第一次初始化。所以说3. +initialize执行时运行时系统处于正常状态
。
在调用栈6 _objc_msgSend_uncached,可以看出来只有当我们在向相关class发送消息的时候,才会最终调用+initialize,所以1. +initialize是惰性调用,只有在使用的时候才会调用
。
查看lookUpImpOrForward源码,判断一个class的+initialize方法是否调用过是检查class对应的meta class的flag属性中RW_INITIALIZED(// class is initialized)标记位是否为YES,如果没有执行+initialize则为NO,在执行完+initialize之后标记位设置为YES,所以2. 只调用一次
:
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
...
if (initialize && !cls->isInitialized()) {
runtimeLock.unlockRead();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.read();
}
...
}
bool isInitialized() {
return getMeta()->data()->flags & RW_INITIALIZED;
}
再看_class_initialize方法,_class_initialize中会递归地调用_class_initialize(cls->supercls)实现先执行超类的initialize方法,然后执行子类的initialize方法。所以4. 会先运行超类的+initialize方法
。
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
// 递归调用父类
_class_initialize(supercls);
}
...
if (reallyInitialize) {
...
{
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
}
...
}
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
我们在父类EOCBaseClass中定义了+initialize方法,子类EOCSubClass中没有定义+initialize方法,在main()函数中我们创建的是子类对象,根据对规则3的分析,在调用堆栈3 ::_class_initialize是对子类EOCSubClass调用,堆栈2 ::_class_initialize是对父类EOCSubClass调用。
在_class_initialize通过callInitialize(cls),在其内部是通过"((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);"调用+initialize方法的,也就是说子类调用的+initialize方法是父类定义的同名方法。所以最后执行结果为:
EOCBaseClass initialize
EOCSubClass initialize
所以解释了规则5. 子类没有实现+initialize方法,会执行超类的+initialize方法
。
对于规则6.不要两个class的+initialize方法中互相使用对方
,在EOCClassA的+initialize中使用EOCClassB,在EOCClassB的+initialize中使用EOCClassA;如果main()函数中使用EOCClassA,则+initialize调用看起来是这样的:
EOCClassA_initialize->EOCClassB_initialize->EOCClassA_initialize
然后在EOCClassA在执行+initialize方法时,EOCClassA的isInitializing()设置为true,而在上述循环调用中再次进入EOCClassA的+initialize中,发现其isInitializing()为true,就直接返回了,所以此时EOCClassB的+initialize方法并没有获得一个完整的EOCClassA。
网友评论