探索 +initialize 底層調用機制 並與+load 比較

前言

在上一篇文章中(手把手帶你探索load底層原理)探索了load的調用機制,本文是探索一下initialize的調用機制,並比較一下異同點。 +initialize其實在平時開發中用的較少,通常用來初始化常量,不過面試中常會問到它與+load的區別。面試

initialize調用規則

  1. +initialize 方法是在類或它的子類收到第一條消息以前被調用的,這裏所指的消息包括實例方法和類方法的調用。也就是說 +initialize 方法是以懶加載的方式被調用的,若是程序一直沒有給某個類或它的子類發送消息,那麼這個類的 +initialize 方法是永遠不會被調用的。
  2. 當子類沒有實現+initialize或者子類在+initialize中顯式的調用了[super initialize],那麼父類的+initialize方法會被調用屢次。
  3. 在分類實現+initialize,會只執行分類的+initialize
  4. 類的+initialize方法在全部父類的+initialize方法調用以後調用

驗證調用規則

此次是在objc源碼中直接調試代碼,在我提供的源碼裏新建一個target, 以下圖 函數

接下來建立Person類,實現loadinitialize方法,編譯運行咱們剛纔建立的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標識),設置完後改成truecdn

  • 這裏有拿到 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 方法,那麼就會對這個類中的實現形成覆蓋。
  • 所以,這裏驗證了開頭的結論二:若是一個子類沒有實現 +initialize 方法,那麼父類的實現是會被執行屢次的

總結

相關文章
相關標籤/搜索