類初始化的時機(jī)
每次調(diào)用類或是對象的方法的時候叛买,會轉(zhuǎn)換成消息發(fā)送棍苹,調(diào)用類或?qū)ο蟮姆椒ㄆ鋵嵕褪墙o類或?qū)ο蟀l(fā)送消息,
發(fā)送消息是通過objc_megSend()系列的方法,然后會調(diào)用 objc-runtime-new.mm 文件里面的lookUpImpOrForward()方法斋攀,這個方法會檢測類是否是已經(jīng)初始化過的狀態(tài)(initialized),如果沒有類沒有初始化梧田,則去初始類蜻韭。
lookUpImpOrForward()方法部分代碼片段如下
//如果類沒有初始化,則初始化類
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
//#define fastpath(x) (__builtin_expect(bool(x), 1))
//#define slowpath(x) (__builtin_expect(bool(x), 0))
//__builtin_expect作用是"允許程序員將最有可能執(zhí)行的分支告訴編譯器"柿扣。這個指令的寫法為:__builtin_expect(EXP, N)肖方。意思是:EXP==N的概率很大。
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
//如果當(dāng)前調(diào)用的方法是initialize方法未状,而且是由messenger(一般是我們自己調(diào)用)調(diào)用俯画,會調(diào)用兩次,但是第一次調(diào)用initialize就會把類標(biāo)記為initialized
}
可以看到,當(dāng)類沒有標(biāo)記為initialized方法的時候司草,會去調(diào)用initializeAndLeaveLocked()方法, initializeAndLeaveLocked()方法最終會調(diào)用initializeAndMaybeRelock()方法
static Class initializeAndMaybeRelock(Class cls, id inst,
mutex_t& lock, bool leaveLocked)
{
lock.assertLocked();
ASSERT(cls->isRealized());
if (cls->isInitialized()) {//如果已經(jīng)執(zhí)行過了初始化方法艰垂,則不調(diào)用
if (!leaveLocked) lock.unlock();
return cls;
}
// Find the non-meta class for cls, if it is not already one.
// The +initialize message is sent to the non-meta class object.
Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// Realize the non-meta class if necessary.
if (nonmeta->isRealized()) {
// nonmeta is cls, which was already realized
// OR nonmeta is distinct, but is already realized
// - nothing else to do
lock.unlock();
} else {
nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
// runtimeLock is now unlocked
// fixme Swift can't relocate the class today,
// but someday it will:
cls = object_getClass(nonmeta);
}
// runtimeLock is now unlocked, for +initialize dispatch
ASSERT(nonmeta->isRealized());
initializeNonMetaClass(nonmeta);//執(zhí)行初始化方法
if (leaveLocked) runtimeLock.lock();
return cls;
}
// Locking: caller must hold runtimeLock; this may drop and re-acquire it
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
return initializeAndMaybeRelock(cls, obj, lock, true);
}
在initializeAndMaybeRelock()方法會判斷類是否已經(jīng)初始化,如果沒有初始化埋虹,會調(diào)用initializeNonMetaClass()方法去初始化類猜憎,并標(biāo)記類為initialized
類初始化流程
從上可知,類是通過initializeNonMetaClass()去初始化一個類的搔课,initializeNonMetaClass()代碼如下
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
初始化類
**********************************************************************/
void initializeNonMetaClass(Class cls)
{
ASSERT(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
//優(yōu)先執(zhí)行父類的initial方法
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
// Try to atomically set CLS_INITIALIZING.
//設(shè)置類正在初始化胰柑,并加鎖
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
{
monitor_locker_t lock(classInitLock);//嘗試對這個類進(jìn)行加鎖,加鎖成功后才會去設(shè)置為狀態(tài)為正在初始化
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
// Grab a copy of the will-initialize funcs with the lock held.
localWillInitializeFuncs.initFrom(willInitializeFuncs);
}
}
if (reallyInitialize) {
//正常進(jìn)入初始化
// We successfully set the CLS_INITIALIZING bit. Initialize the class
// Record that we're initializing this class so we can message it.
//標(biāo)記當(dāng)前線程正在初始化該類
_setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
for (auto callback : localWillInitializeFuncs)
callback.f(callback.context, cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
#if __OBJC2__
@try
#endif
{
//假設(shè)子類沒有實現(xiàn)initialize方法爬泥,會調(diào)用父類的initialize方法
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
objc_thread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
//標(biāo)記初始化完成
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
//類正在初始化
//如果該類正在初始化柬讨,而且是當(dāng)前線程在初始化,則不執(zhí)行任何操作
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
//等待線程初始化
return;
} else {
//如果是別的線程正在初始化該類
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
else if (cls->isInitialized()) {
//如果該類已經(jīng)被初始化了袍啡,就不執(zhí)行任何操作
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
在initializeNonMetaClass()的開始踩官,有一個對該方法的遞歸調(diào)用
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
這里的作用是當(dāng)類存在父類的時候,會去優(yōu)先初始化父類境输。當(dāng)類初始化完成的時候蔗牡,再一層一層的往下初始化類颖系。
這里有個問題,類什么時候算是初始化完成辩越?記住偷拔,當(dāng)一個類被標(biāo)記為initialized狀態(tài)的時候姑丑,類就算是初始化完成,而不是等到類的+initialize()方法執(zhí)行完才算是初始化完成。
一個類關(guān)于初始化有三個狀態(tài),未初始化豪嚎,正在初始化(nitializing)糯耍,初始化完成(initialized)。
初始化的時候會根據(jù)類的初始化狀態(tài)設(shè)置reallyInitialize變量焊刹,當(dāng)reallyInitialize變量的值為YES,就進(jìn)入到真正的初始化,調(diào)用callInitialize()方法,去調(diào)用我們定義的+ initialize()方法,然后再調(diào)用lockAndFinishInitializing()方法以清,將類設(shè)置為初始化完成狀態(tài)initialized个绍。
callInitialize()方法定義如下
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
這個方法中也是通過消息發(fā)送機(jī)制objc_msgSend去調(diào)用我們定義的+ initialize()方法隅茎。因為是通過消息發(fā)送機(jī)制去調(diào)用的,所以當(dāng)一個類沒有定義+ initialize()方法的時候篷牌,會調(diào)用父類的+ initialize()方法睡蟋,所以一個類的+ initialize()方法有可能會被調(diào)用多次。但是只有在第一次調(diào)用+ initialize()才會把類的初始化狀態(tài)設(shè)置為initialized枷颊。
執(zhí)行完以后就要通過調(diào)用lockAndFinishInitializing()設(shè)置類為initilaized戳杀,實現(xiàn)如下
static void lockAndFinishInitializing(Class cls, Class supercls)
{
monitor_locker_t lock(classInitLock);
if (!supercls || supercls->isInitialized()) {
_finishInitializing(cls, supercls);
} else {
_finishInitializingAfter(cls, supercls);
}
}
static void _finishInitializing(Class cls, Class supercls)
{
PendingInitialize *pending;
classInitLock.assertLocked();
ASSERT(!supercls || supercls->isInitialized());
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: %s is fully +initialized",
objc_thread_self(), cls->nameForLogging());
}
// mark this class as fully +initialized
cls->setInitialized();//標(biāo)志為inititaled狀態(tài)
classInitLock.notifyAll();//釋放鎖
_setThisThreadIsNotInitializingClass(cls);
// mark any subclasses that were merely waiting for this class
if (!pendingInitializeMap) return;
auto it = pendingInitializeMap->find(cls);
if (it == pendingInitializeMap->end()) return;//如果pendingInitializeMap只有一個元素,則直接返回·
pending = it->second;
pendingInitializeMap->erase(it);
// Destroy the pending table if it's now empty, to save memory.
if (pendingInitializeMap->size() == 0) {
delete pendingInitializeMap;
pendingInitializeMap = nil;
}
//遞歸調(diào)用夭苗,將相同父類而且完成了初始化方法信卡,但是狀態(tài)未被標(biāo)記成Initialied的類標(biāo)記成Initialied
while (pending) {
PendingInitialize *next = pending->next;
if (pending->subclass) _finishInitializing(pending->subclass, cls);
delete pending;
pending = next;
}
}
static void _finishInitializingAfter(Class cls, Class supercls)
{
classInitLock.assertLocked();
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: class %s will be marked as fully "
"+initialized after superclass +[%s initialize] completes",
objc_thread_self(), cls->nameForLogging(),
supercls->nameForLogging());
}
if (!pendingInitializeMap) {
pendingInitializeMap = new PendingInitializeMap{10};
// fixme pre-size this table for CF/NSObject +initialize
}
PendingInitialize *pending = new PendingInitialize{cls};
auto result = pendingInitializeMap->try_emplace(supercls, pending);
if (!result.second) {
pending->next = result.first->second;
result.first->second = pending;
}
}
lockAndFinishInitializing()方法中有一個判斷 (!supercls || supercls->isInitialized()),判斷是否不存在父類或是存在父類且父類已經(jīng)完成初始化题造。
- 如果是傍菇,則調(diào)用_finishInitializing(),_finishInitializing()的作用是標(biāo)記類完成初始化界赔。
- 否則調(diào)用_finishInitializingAfter()丢习。
前面不是說類的初始化initializeNonMetaClass()方法通過遞歸調(diào)用牵触,可以確保父類會優(yōu)先執(zhí)行嗎?怎么會出現(xiàn)子類執(zhí)行l(wèi)ockAndFinishInitializing()的時候父類還沒完成初始化的情況呢咐低?
其實根據(jù)面向?qū)ο蟮乃枷肜克迹割惖亩x中是不存在關(guān)于任何關(guān)于子類的內(nèi)容的,如下
见擦。
但是有些特殊情況钉汗,會在父類的+initialize()方法中去調(diào)用子類的方法,如下
當(dāng)我們執(zhí)行[[ClassB alloc] init]方法的時候鲤屡,會想ClassB發(fā)送消息损痰,調(diào)用ClassB的initializeNonMetaClass(),接著會調(diào)用ClassA的initializeNonMetaClass(),在Class A中調(diào)用子類Class C的alloc方法执俩,就是向子類Class C發(fā)送消息,由于子類Class C還沒有初始化徐钠,所以會調(diào)用initializeNonMetaClass(),如下圖中的 調(diào)用1役首。
然后就又回遞歸調(diào)用尝丐,一直調(diào)用到ClassA的initializeNonMetaClass()方法。如下圖中的 調(diào)用2
這時候執(zhí)行到Class A的initializeNonMetaClass()方法衡奥,由于ClassA正在初始化(當(dāng)執(zhí)行第一次initializeNonMetaClass()方法的時候爹袁,會把當(dāng)前類的初始化狀態(tài)設(shè)置為),會跳到
(cls->isInitializing()) {
//類正在初始化
// We couldn't set INITIALIZING because INITIALIZING was already set.
// If this thread set it earlier, continue normally.
// If some other thread set it, block until initialize is done.
// It's ok if INITIALIZING changes to INITIALIZED while we're here,
// because we safely check for INITIALIZED inside the lock
// before blocking.
//如果該類正在初始化矮固,而且是當(dāng)前線程在初始化失息,則不執(zhí)行任何操作
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);
}
}
然后會判斷是否是當(dāng)前線程在初始化這個類,由于在前面我們是通過遞歸調(diào)用的档址,所以是在同一個線程(為什么需要判斷是否是當(dāng)前線程盹兢?由于設(shè)計到多線程,等會繼續(xù)說一下這里的處理)守伸。所以_thisThreadIsInitializingClass()成立绎秒,就直接返回。
這時候就會回到ClassB的initializeNonMetaClass()方法 ,如下圖 調(diào)用3尼摹。這時候由于之前的ClassB沒有初始化過见芹,reallyInitialize為YES,這時候就正常進(jìn)入到初始化流程蠢涝。
先調(diào)用callInitialize()方法玄呛,然后再調(diào)用lockAndFinishInitializing()方法,這時候注意了和二,ClassA正處于 正在初始化(initializing)的階段徘铝,在調(diào)用ClassB的lockAndFinishInitializing()方法的時候,由于父類還沒初始化完成,就會_finishInitializingAfter()方法庭砍,
_finishInitializingAfter()方法不會將類的狀態(tài)設(shè)置為initalized,只會把當(dāng)前類存入到pendingInitializeMap中场晶。pendingInitializeMap是一個存放那些完成了初始化方法混埠,但是父類還沒標(biāo)記成完成初始化狀態(tài)initalized的類的容器怠缸。
ClassC也和ClassB一樣,由于ClassC的初始化狀態(tài)不是initalized钳宪,所以會執(zhí)行callInitialize()方法揭北,然后再調(diào)用_finishInitializingAfter()方法,把類B存入到pendingInitializeMap中
最后回到ClassA的initializeNonMetaClass()方法吏颖,并調(diào)用lockAndFinishInitializing()方法搔体,由于ClassA沒有父類,所以最終會調(diào)用_finishInitializing()方法半醉。_finishInitializing()方法中先是把ClassA的狀態(tài)設(shè)置為initalized疚俱。然后通過遞歸在pendingInitializeMap找到以ClassA為父類的ClassB,并把ClassB的狀態(tài)設(shè)置為initalized缩多。再把以ClassB為父類的ClassC設(shè)置為initalized呆奕。
從上面可以看出,ClassA的+initialize()方法在ClassB的+initialize()前執(zhí)行衬吆,ClassB的+initialize()方法在ClassC的+initialize()前執(zhí)行梁钾。這樣就確保了一條規(guī)則
- 父類的+initialize()無論在什么情況下都會比子類的+initialize()優(yōu)先執(zhí)行。
但是從上面也可以看出逊抡,ClassA的+initialize()方法在還沒有執(zhí)行完畢的時候姆泻,ClassB和ClassC的+initialize()已經(jīng)執(zhí)行完畢了。所以會有以下的一條規(guī)則
- 當(dāng)父類的+initialize()調(diào)用了子類的方法的時候冒嫡,子類的+initialize()會比父類的+initialize()方法先執(zhí)行完畢
多線程與初始化
在initializeNonMetaClass()方法中涉及到了一些線程處理拇勃,在類的初始化階段為什么需要線程操作呢?
先來看以下的情況
存在ClassA,ClassB,ClassC,ClassD,ClassE五個類孝凌,繼承關(guān)系如上圖所示
在不考慮線程處理的情況下方咆,執(zhí)行代碼
NSThread *t1 = [[NSThread alloc] initWithBlock:^{
[[ClassC alloc] init];
}];
NSThread *t2 = [[NSThread alloc] initWithBlock:^{
[[ClassD alloc] init];
}];
[t2 start];
[t1 start];
開啟兩個線程,初始化ClassC 和 ClassD胎许,會出現(xiàn)什么問題呢峻呛?
這個問題有可能出現(xiàn)死鎖,為什么呢辜窑?
從類的初始化 流程可知钩述,當(dāng)父類+initiale()方法中有調(diào)用子類的方法的時候,遞歸調(diào)用initializeNonMetaClass()是會出現(xiàn)環(huán)形的穆碎。
當(dāng)我們在線程1向ClassD發(fā)送消息的時候牙勘,會調(diào)用initializeNonMetaClass()方法。然后這時候另外一個線程2向ClassC發(fā)送消息,然后調(diào)用initializeNonMetaClass()了方面,ClassC的initializeNonMetaClass()方法調(diào)用了ClassB的initializeNonMetaClass(),ClassB的initializeNonMetaClass()調(diào)用了ClassA的initializeNonMetaClass()放钦。
這時候ClassA中的+initialze()方法中會向ClassE發(fā)送消息,從而調(diào)用ClassE的initializeNonMetaClass()方法恭金。而ClassE的initializeNonMetaClass()會調(diào)用ClassD的initializeNonMetaClass()方法
線程2中的ClassE需要等到線程1中的ClassD初始化完成才能繼續(xù)走下去操禀。而線程1中的ClassD需要等到線程2中的ClassB初始化完成才能走下去,而線程2中的ClassB需要等到等到ClassE中初始化完成才走下去横腿。這樣颓屑,就形成了死鎖,如下
那objc庫是怎么解決這個問題呢?objc中是通過加鎖去保證當(dāng)前線程初始化的連續(xù)性耿焊。連續(xù)性是當(dāng)一個線程對類初始化的時候揪惦,類本身和類的所有負(fù)類的初始化工作全部交由該線程處理。別的線程如果向初始化該類或是父類罗侯,都會阻塞器腋。
在不考慮線程優(yōu)先級的情況下,當(dāng)兩個不同的線程去初始化具有相同父類的類的時候钩杰,先執(zhí)行初始化流程的線程會保證初始化的連續(xù)性纫塌。為什么呢?
因為初始化方法initializeNonMetaClass()方法榜苫,會通過遞歸方式去調(diào)用父類的initializeNonMetaClass()护戳。當(dāng)遞歸條件不成立,也就是類已經(jīng)沒有父類的情況下(根類)或是父類已經(jīng)初始化完成的情況下垂睬。才會進(jìn)入到初始化的執(zhí)行階段媳荒。
void initializeNonMetaClass(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.
//優(yōu)先執(zhí)行父類的initial方法
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
// Try to atomically set CLS_INITIALIZING.
//設(shè)置類正在初始化,并枷鎖
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
{
monitor_locker_t lock(classInitLock);//嘗試對這個類進(jìn)行加鎖驹饺,加鎖成功后才會去設(shè)置為狀態(tài)為正在初始化
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
// Grab a copy of the will-initialize funcs with the lock held.
localWillInitializeFuncs.initFrom(?willInitializeFuncs);
}
}
...正式進(jìn)入初始化工作...
}
從代碼的實現(xiàn)可知钳枕,在正式進(jìn)入初始化工作前,會對代碼進(jìn)行加鎖
monitor_locker_t lock(classInitLock)
如果線程一旦加鎖成功赏壹,在完成初始化并釋放掉鎖之前鱼炒。別的線程就沒有辦法去加鎖,就會阻塞蝌借,就是沒有辦法進(jìn)入初始化流程昔瞧。那么該線程就可以處理類的初始化流程并不受別的線程打斷。
換言之菩佑,就是哪個線程可以優(yōu)先使沒有初始化的父類進(jìn)入到正在初始化的階段自晰,那么該線程就會負(fù)責(zé)該類的所有負(fù)類的初始化工作,并不受別的線程影響
其他
當(dāng)線程調(diào)用初始化方法initializeNonMetaClass()并進(jìn)入reallyInitialize條件時稍坯,就會執(zhí)行callInitialize()方法酬荞,調(diào)用我們定義的+initialze()方法。
但在callInitialize()前,會執(zhí)行_setThisThreadIsInitializingClass()方法,這個方法的作用是把當(dāng)前類添加到當(dāng)前線程的_objc_initializing_classes中混巧。
_objc_initializing_classes是指該線程正在初始化的類的集合枪向。