Objc源码之对象创建alloc和init
Objc源码之initialize实现
Objc源码之Load方法实现
Objc源码之NSObject和isa
Objc源码之引用计数实现
objc源码之Method消息发送
前言
提到initialize方法,我们总会拿它和load方法作对比,那是因为这两个方法都是系统调用,且比较靠前,可以再类还没初始化的时候,做一些事情,关于两者的调用时机和差异点,大家可以看下我的这两篇文章iOS中load和initialize方法总结,还有Runtime源码之Load方法实现,这篇文章主要来分析initialize方法的源码实现。
接下来,我会从源码的角度,来说明一下几个问题:
- initialize方法调用时机
- initialize的调用顺序
注:本文分析基于objc4-750源码进行的。
一、initialize方法调用时机
1.initialize方法调用栈
首先,我们创建一个对象TestObject,添加initialize
方法,并在initialize
方法中打上断点,这时我们可以看到下图的调用栈,我们可以看到initialize
函数是通过lookUpImpOrForward
调用,并最终调用_class_initialize
函数,来调用方法中的initialize
函数,调用栈如下图所示:
由上面的调用栈,我们可以知道,initialize的调用是在main函数的后面执行,在方法调用的
_objc_msgSend_uncached
时候,需要调用方法查找函数lookUpImpOrForward
,而lookUpImpOrForward
里面会调用_class_initialize
进行初始类,最终调用initialize
。下面,我们就重点分析一下lookUpImpOrForward
和_class_initialize
的源码实现。
2.lookUpImpOrForward和_class_initialize源码实现
首先我们看下lookUpImpOrForward
源码实现:
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// 1.在缓存中查找方法
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
runtimeLock.lock();
checkIsKnownClass(cls);
if (!cls->isRealized()) {
realizeClass(cls);
}
//2.类是否初始化过
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
}
省略......
}
我们知道lookUpImpOrForward
是在调用方法时,进行消息转发的时候,会调用的,再调用具体方法的时候,会先判断类是否初始化过,通过cls->isInitialized()
来进行判断,如果还没有被初始化过,就会调用_class_initialize
来调用initialize
方法。
bool isInitialized() {
return getMeta()->data()->flags & RW_INITIALIZED;
}
类的初始化信息,记录在元类class_rw_t
结构体中的 flags
中,所以这里cls->isInitialized()
是通过读取flags值来判断是否初始化过的。
如果没有初始化过,那么就调用_class_initialize
方法,这是调用initialize和信方法:
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// 1.判断父类是否初始化
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// 2.判断是否已经初始化或者正在初始化,设置CLS_INITIALIZING
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
if (reallyInitialize) {
// 3.记录初始化类的线程,只有这个这个线程可以给类发送消息,其他线程等待
_setThisThreadIsInitializingClass(cls);
//4.子线程fork时父进程是多线程的,执行fork子线程的Initialize
if (MultithreadedForkChild) {
performForkChildInitialize(cls, supercls);
return;
}
{
//5.调用Initialize方法
callInitialize(cls);
}
{
// 6.设置Initialized
lockAndFinishInitializing(cls, supercls);
}
return;
}
//7.正在初始化
else if (cls->isInitializing()) {
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
//8.已经初始化 返回
else if (cls->isInitialized()) {
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
1)判断父类是否初始化,如果没有初始化,递归调用父类初始化。
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
2)判断类是否已经初始化或者正在初始化,设置CLS_INITIALIZING
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
3)记录初始化类的线程,只有这个这个线程可以给类发送消息,其他线程等待,这个在第七步的时候,_thisThreadIsInitializingClass会用到。
_setThisThreadIsInitializingClass(cls);
4)子线程fork时父进程是多线程的,执行fork子线程的Initialize
if (MultithreadedForkChild) {
performForkChildInitialize(cls, supercls);
return;
}
5)调用callInitialize方法,执行Initialize方法
callInitialize(cls);
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
6)设置Initialized, 如果父类已经初始化完成,设置标志位。否则,在父类初始化完成之后再设置标志位.
lockAndFinishInitializing(cls, supercls);
static void lockAndFinishInitializing(Class cls, Class supercls)
{
monitor_locker_t lock(classInitLock);
if (!supercls || supercls->isInitialized()) {
_finishInitializing(cls, supercls);
} else {
_finishInitializingAfter(cls, supercls);
}
}
7)类正在初始化,判断类在哪个线程初始化,如果在当前线程初始化(通过第3步记录的值判断),直接返回,如果在多线程不是fork的,等待初始化完成,如果是子线程fork时父进程是多线程的,执行performForkChildInitialize
,和4)相同的操作。
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
8)如果已经初始化成功,直接返回
else if (cls->isInitialized()) {
return;
}
二、initialize的调用顺序
从_class_initialize
中的可以看出来,父类是优先于子类调用。
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
initialize执行顺序总结
1.如果类和类Category都实现了initialize方法,调用Category的initialize方法,会覆盖类中的方法,只执行一个,如果多个category,则调用编译顺序最后的initialize方法。
2.如果父类和子类都实现了initialize方法,
在调用子类时,如果父类的initialize方法调用过,则只调用子类initialize方法。
如果父类没用过,则先调用父类的Category的initialize方法,在调用子类的initialize方法
如果调用子类的时候,已经初始化过父类的initialize,则在初始化父类的时候,不会再调用initialize方法
3.如果子类没有实现initialize方法,父类实现了initialize方法,调用子类的时候,会先调用父类的initialize方法,再调用子类的实例方法。
总结:initialized方法是在第一次向类发送消息时调用的,通过lookUpImpOrForward
最终调用到callInitialize
的,从整个过程可以看出来initialize是多线程安全的,并且保证父类优先于子类调用。
参考:
objc4-750源码
nsobject-load-and-initialize-what-do-they-do
iOS中load和initialize方法总结
懒惰的 initialize 方法.md
网友评论