在上一篇文章中(手把手帶你探索load底層原理)探索了load的調用機制,本文是探索一下initialize的調用機制,並比較一下異同點。 +initialize
其實在平時開發中用的較少,通常用來初始化常量,不過面試中常會問到它與+load
的區別。面試
+initialize
方法是在類或它的子類收到第一條消息以前被調用的,這裏所指的消息包括實例方法和類方法的調用。也就是說 +initialize
方法是以懶加載的方式被調用的,若是程序一直沒有給某個類或它的子類發送消息,那麼這個類的 +initialize
方法是永遠不會被調用的。+initialize
或者子類在+initialize
中顯式的調用了[super initialize]
,那麼父類的+initialize
方法會被調用屢次。+initialize
,會只執行分類的+initialize
+initialize
方法在全部父類的+initialize
方法調用以後調用此次是在objc
源碼中直接調試代碼,在我提供的源碼裏新建一個target
, 以下圖 函數
接下來建立Person
類,實現load
和initialize
方法,編譯運行咱們剛纔建立的target
post
運行結果只執行了load
方法,並無執行initialize
方法,因此可以獲得結論:load
在文件被加載時就執行了,initialize
不主動調用atom
對於本文開頭的幾個結論,能夠在這裏都簡單用代碼驗證一遍,我這裏就再也不去一一驗證了,直接開始探索源碼。spa
我在load
裏打印了一下self
,發現initialize
被調用了,那麼看看斷點處的initialize
3d
發現這裏其實就進入到了runtime的消息查找流程,這裏直接來到第三步,這裏判斷是否類已經初始化,沒有初始化就作初始化操做。因此在這裏能夠驗證開頭的結論一,它是收到類的第一條消息後才調用的。 調試
看看_class_initialize
函數code
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);
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
//其他代碼省略
callInitialize(cls);
複製代碼
這裏有一個標識符reallyInitialize
,在cls->setInitializing()
(給類的isa設置RW_INITIALIZING
標識),設置完後改成true
cdn
這裏有拿到 supercls
,而後判斷是否存在和是否初始化完成,未初始化則調用本身自己這個函數_class_initialize
,其實就是一個遞歸,用來確保全部父類的initialize
已經執行完,再執行當前類,這裏和以前探索load
的時候的遞歸調用父類load
的方法原理相同。因此在這裏能夠驗證開頭的結論四:類的+initialize
方法在全部父類的+initialize
方法調用以後調用。blog
void callInitialize(Class cls) {
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
複製代碼
callInitialize
, 使用了發送消息 objc_msgSend
的方式對 +initialize
方法進行調用。也就是說 +initialize
方法的調用與普通方法的調用是同樣的,走的都是發送消息的流程。換言之,若是子類沒有實現 +initialize
方法,那麼繼承自父類的實現會被調用;因此有告終論三:若是一個類的分類實現了 +initialize
方法,那麼就會對這個類中的實現形成覆蓋。