initialize方法
- 调用时机:initialize在类第一次接收到消息时调用,也就是objc_msgSend(),当一个类在查找方法的时候,会先判断当前类是否初始化,如果没有初始化就会去调用initialize方法。
- 调用顺序:先调用父类的+initialize,再调用类放入initialize。
- 调用次数:有可能是多次
- 分类中的实现:只执行分类的调用
- 类在调用initalize时,使用的是objc_msgSend消息机制调用
- 作用:初始化操作
可以自己写代码举例证明,这里只是通过runtime源码,去了解一下。
首先,在runtime源码中,我们创建了一个Person类,在Person类的initalize方法中进行打印,并且加上断点:
屏幕快照 2019-09-22 下午9.33.42.png
- 直接运行,当然是没有任何输出的,因为上面有提到initialize的调用时机,initialize在类第一次接收到消息时调用
-
main.m 文件中,调用Person 的class方法:
屏幕快照 2019-09-22 下午9.35.18.png
此时,我们会看到走到了断点处:
屏幕快照 2019-09-22 下午9.36.41.png
从上图中我们可以看到,main函数执行之后的流程是:
- _objc_msgSend_uncached
- _class_lookupMethodAndLoadCache3
- lookUpImpOrForward
- _class_initialize
- callInitialize
- [Person initialize]
我们直接从第3步看就行了,下面是lookUpImpOrForward的方法代码:
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
... // 判断这个类是否初始化
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.lock();
// 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
}
...
}
cls->isInitialized() 其实就是一个标记这个类是否初始化的标志:
bool isInitialized() {
return getMeta()->data()->flags & RW_INITIALIZED;
}
此时我们继续看 _class_initialize 这个方法:
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// 先调用父类的,再调用子类的
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
// 递归调用
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
#if __OBJC2__
@try
#endif
{
// 调用 cls 的initalize 方法
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
pthread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
// We're on the child side of fork(), facing a class that
// was initializing by some other thread when fork() was called.
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
else if (cls->isInitialized()) {
return;
}
else {
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
继续看callInitialize 方法:
void callInitialize(Class cls)
{
// 消息查找流程
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
所以到此非常明了,从源码中我们可以看到
- 先调用父类的initialize方法,再调用子类的initalize方法,由于initalize方法会走消息查找流程,所以当分类中也实现了initialize方法之后,只会执行分类的initalize方法。
- 如果本类没有实现initialize方法,父类实现了initialize方法,则多个子类初始化时会多次调用父类的initialize方法,但是本质上只有第一次initialize方法是初始化父类,后面几个initialize都是方法的调用,即子类没有实现,通过superclass到父类里查找。
+initialize和+load的一个很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点:
1、如果子类没有实现+initialize方法,会调用父类的+initialize(所以父类的+initialize方法可能会被调用多次)
2、如果分类实现了+initialize,会覆盖类本身的+initialize调用。
网友评论