探秘Runtime - Runtime加載過程

該文章屬于劉小壯原創(chuàng),轉(zhuǎn)載請(qǐng)注明:劉小壯


程序加載過程

iOS程序中會(huì)用到很多系統(tǒng)的動(dòng)態(tài)庫,這些動(dòng)態(tài)庫都是動(dòng)態(tài)加載的。所有iOS程序共用一套系統(tǒng)動(dòng)態(tài)庫,在程序開始運(yùn)行時(shí)才會(huì)開始鏈接動(dòng)態(tài)庫。

the dynamic link editor

除了在項(xiàng)目設(shè)置里顯式出現(xiàn)的動(dòng)態(tài)庫外,還會(huì)有一些隱式存在的動(dòng)態(tài)庫。例如objcRuntime所屬的libobjc.dyldlibSystem.dyld,在libSystem中包含常用的libdispatch(GCD)libsystem_c(C語言基礎(chǔ)庫)、libsystem_blocks(Block)等。

使用動(dòng)態(tài)庫的優(yōu)點(diǎn):

  1. 防止重復(fù)。iOS系統(tǒng)中所有App公用一套系統(tǒng)動(dòng)態(tài)庫,防止重復(fù)的內(nèi)存占用。
  2. 減少包體積。因?yàn)橄到y(tǒng)動(dòng)態(tài)庫被內(nèi)置到iOS系統(tǒng)中,所以打包時(shí)不需要把這部分代碼打進(jìn)去,可以減小包體積。
  3. 動(dòng)態(tài)性。因?yàn)橄到y(tǒng)動(dòng)態(tài)庫是動(dòng)態(tài)加載的,所以可以在更新系統(tǒng)后,將動(dòng)態(tài)庫換成新的動(dòng)態(tài)庫。

加載過程

在應(yīng)用程序啟動(dòng)后,由dyld(the dynamic link editor)進(jìn)行程序的初始化操作。大概流程就像下面列出的步驟,其中第3、4、5步會(huì)執(zhí)行多次,在ImageLoader加載新的image進(jìn)內(nèi)存后就會(huì)執(zhí)行一次。

  1. 在引用程序啟動(dòng)后,由dyld將應(yīng)用程序加載到二進(jìn)制中,并完成一些文件的初始化操作。
  2. Runtimedyld中注冊(cè)回調(diào)函數(shù)。
  3. 通過ImageLoader將所有image加載到內(nèi)存中。
  4. dyldimage發(fā)生改變時(shí),主動(dòng)調(diào)用回調(diào)函數(shù)。
  5. Runtime接收到dyld的函數(shù)回調(diào),開始執(zhí)行map_imagesload_images等操作,并回調(diào)+load方法。
  6. 調(diào)用main()函數(shù),開始執(zhí)行業(yè)務(wù)代碼。

ImageLoaderimage的加載器,image可以理解為編譯后的二進(jìn)制。

下面是在Runtimemap_images函數(shù)打斷點(diǎn),觀察回調(diào)情況的匯編代碼。可以看出,調(diào)用是由dyld發(fā)起的,由ImageLoader通知dyld進(jìn)行調(diào)用。

匯編調(diào)用

關(guān)于dyld我并沒有深入研究,有興趣的同學(xué)可以到Github上下載源碼研究一下。

動(dòng)態(tài)加載

一個(gè)OC程序可以在運(yùn)行過程中動(dòng)態(tài)加載和鏈接新類或Category,新類或Category會(huì)加載到程序中,其處理方式和其他類是相同的。動(dòng)態(tài)加載還可以做許多不同的事,動(dòng)態(tài)加載允許應(yīng)用程序進(jìn)行自定義處理。

OC提供了objc_loadModules運(yùn)行時(shí)函數(shù),執(zhí)行Mach-O中模塊的動(dòng)態(tài)加載,在上層NSBundle對(duì)象提供了更簡單的訪問API

map images

Runtime加載時(shí),會(huì)調(diào)用_objc_init函數(shù),并在內(nèi)部注冊(cè)三個(gè)函數(shù)指針。其中map_images函數(shù)是初始化的關(guān)鍵,內(nèi)部完成了大量Runtime環(huán)境的初始化操作。

map_images函數(shù)中,內(nèi)部也是做了一個(gè)調(diào)用中轉(zhuǎn)。然后調(diào)用到map_images_nolock函數(shù),內(nèi)部核心就是_read_images函數(shù)。

void _objc_init(void)
{
    // .... 各種init
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

void map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    rwlock_writer_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
    if (hCount > 0) {
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
}

_read_images函數(shù)中完成了大量的初始化操作,函數(shù)內(nèi)部代碼量比較大,下面是精簡版帶注釋的源代碼。

先整體梳理一遍_read_images函數(shù)內(nèi)部的邏輯:

  1. 加載所有類到類的gdb_objc_realized_classes表中。
  2. 對(duì)所有類做重映射。
  3. 將所有SEL都注冊(cè)到namedSelectors表中。
  4. 修復(fù)函數(shù)指針遺留。
  5. 將所有Protocol都添加到protocol_map表中。
  6. 對(duì)所有Protocol做重映射。
  7. 初始化所有非懶加載的類,進(jìn)行rw、ro等操作。
  8. 遍歷已標(biāo)記的懶加載的類,并做初始化操作。
  9. 處理所有Category,包括ClassMeta Class
  10. 初始化所有未初始化的類。
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    header_info *hi;
    uint32_t hIndex;
    size_t count;
    size_t i;
    Class *resolvedFutureClasses = nil;
    size_t resolvedFutureClassCount = 0;
    static bool doneOnce;
    TimeLogger ts(PrintImageTimes);

#define EACH_HEADER \
    hIndex = 0;         \
    hIndex < hCount && (hi = hList[hIndex]); \
    hIndex++

    if (!doneOnce) {
        doneOnce = YES;
        // 實(shí)例化存儲(chǔ)類的哈希表,并且根據(jù)當(dāng)前類數(shù)量做動(dòng)態(tài)擴(kuò)容
        int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
    }

    // 由編譯器讀取類列表,并將所有類添加到類的哈希表中,并且標(biāo)記懶加載的類并初始化內(nèi)存空間
    for (EACH_HEADER) {
        if (! mustReadClasses(hi)) {
            continue;
        }

        bool headerIsBundle = hi->isBundle();
        bool headerIsPreoptimized = hi->isPreoptimized();

        /** 將新類添加到哈希表中 */
        
        // 從編譯后的類列表中取出所有類,獲取到的是一個(gè)classref_t類型的指針
        classref_t *classlist = _getObjc2ClassList(hi, &count);
        for (i = 0; i < count; i++) {
            // 數(shù)組中會(huì)取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunloop等系統(tǒng)類,例如CF、Fundation、libdispatch中的類。以及自己創(chuàng)建的類
            Class cls = (Class)classlist[i];
            // 通過readClass函數(shù)獲取處理后的新類,內(nèi)部主要操作ro和rw結(jié)構(gòu)體
            Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);

            // 初始化所有懶加載的類需要的內(nèi)存空間
            if (newCls != cls  &&  newCls) {
                // 將懶加載的類添加到數(shù)組中
                resolvedFutureClasses = (Class *)
                    realloc(resolvedFutureClasses, 
                            (resolvedFutureClassCount+1) * sizeof(Class));
                resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
            }
        }
    }
    
    // 將未映射Class和Super Class重映射,被remap的類都是非懶加載的類
    if (!noClassesRemapped()) {
        for (EACH_HEADER) {
            // 重映射Class,注意是從_getObjc2ClassRefs函數(shù)中取出類的引用
            Class *classrefs = _getObjc2ClassRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[i]);
            }
            // 重映射父類
            classrefs = _getObjc2SuperRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[i]);
            }
        }
    }

    // 將所有SEL都注冊(cè)到哈希表中,是另外一張哈希表
    static size_t UnfixedSelectors;
    sel_lock();
    for (EACH_HEADER) {
        if (hi->isPreoptimized()) continue;

        bool isBundle = hi->isBundle();
        SEL *sels = _getObjc2SelectorRefs(hi, &count);
        UnfixedSelectors += count;
        for (i = 0; i < count; i++) {
            const char *name = sel_cname(sels[i]);
            // 注冊(cè)SEL的操作
            sels[i] = sel_registerNameNoLock(name, isBundle);
        }
    }

    // 修復(fù)舊的函數(shù)指針調(diào)用遺留
    for (EACH_HEADER) {
        message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
        if (count == 0) continue;
        for (i = 0; i < count; i++) {
            // 內(nèi)部將常用的alloc、objc_msgSend等函數(shù)指針進(jìn)行注冊(cè),并fix為新的函數(shù)指針
            fixupMessageRef(refs+i);
        }
    }

    // 遍歷所有協(xié)議列表,并且將協(xié)議列表加載到Protocol的哈希表中
    for (EACH_HEADER) {
        extern objc_class OBJC_CLASS_$_Protocol;
        // cls = Protocol類,所有協(xié)議和對(duì)象的結(jié)構(gòu)體都類似,isa都對(duì)應(yīng)Protocol類
        Class cls = (Class)&OBJC_CLASS_$_Protocol;
        assert(cls);
        // 獲取protocol哈希表
        NXMapTable *protocol_map = protocols();
        bool isPreoptimized = hi->isPreoptimized();
        bool isBundle = hi->isBundle();

        // 從編譯器中讀取并初始化Protocol
        protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map, 
                         isPreoptimized, isBundle);
        }
    }
    
    // 修復(fù)協(xié)議列表引用,優(yōu)化后的images可能是正確的,但是并不確定
    for (EACH_HEADER) {
        // 需要注意到是,下面的函數(shù)是_getObjc2ProtocolRefs,和上面的_getObjc2ProtocolList不一樣
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapProtocolRef(&protolist[i]);
        }
    }

    // 實(shí)現(xiàn)非懶加載的類,對(duì)于load方法和靜態(tài)實(shí)例變量
    for (EACH_HEADER) {
        classref_t *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            // 實(shí)現(xiàn)所有非懶加載的類(實(shí)例化類對(duì)象的一些信息,例如rw)
            realizeClass(cls);
        }
    }

    // 遍歷resolvedFutureClasses數(shù)組,實(shí)現(xiàn)所有懶加載的類
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            // 實(shí)現(xiàn)懶加載的類
            realizeClass(resolvedFutureClasses[i]);
            resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }

    // 發(fā)現(xiàn)和處理所有Category
    for (EACH_HEADER) {
        // 外部循環(huán)遍歷找到當(dāng)前類,查找類對(duì)應(yīng)的Category數(shù)組
        category_t **catlist = 
            _getObjc2CategoryList(hi, &count);
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();

        // 內(nèi)部循環(huán)遍歷當(dāng)前類的所有Category
        for (i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);

            // 首先,通過其所屬的類注冊(cè)Category。如果這個(gè)類已經(jīng)被實(shí)現(xiàn),則重新構(gòu)造類的方法列表。
            bool classExists = NO;
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                // 將Category添加到對(duì)應(yīng)Class的value中,value是Class對(duì)應(yīng)的所有category數(shù)組
                addUnattachedCategoryForClass(cat, cls, hi);
                // 將Category的method、protocol、property添加到Class
                if (cls->isRealized()) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
            }

            // 這塊和上面邏輯一樣,區(qū)別在于這塊是對(duì)Meta Class做操作,而上面則是對(duì)Class做操作
            // 根據(jù)下面的邏輯,從代碼的角度來說,是可以對(duì)原類添加Category的
            if (cat->classMethods  ||  cat->protocols  
                ||  (hasClassProperties && cat->_classProperties)) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
            }
        }
    }

    // 初始化從磁盤中加載的所有類,發(fā)現(xiàn)Category必須是最后執(zhí)行的
    // 從runtime objc4-532版本源碼來看,DebugNonFragileIvars字段一直是-1,所以不會(huì)進(jìn)入這個(gè)方法中
    if (DebugNonFragileIvars) {
        realizeAllClasses();
    }
#undef EACH_HEADER
}

其內(nèi)部還調(diào)用了很多其他函數(shù),后面會(huì)詳細(xì)介紹函數(shù)內(nèi)部實(shí)現(xiàn)。

load images

在項(xiàng)目中經(jīng)常用到load類方法,load類方法的調(diào)用時(shí)機(jī)比main函數(shù)還要靠前。load方法是由系統(tǒng)來調(diào)用的,并且在整個(gè)程序運(yùn)行期間,只會(huì)調(diào)用一次,所以可以在load方法中執(zhí)行一些只執(zhí)行一次的操作。

一般Method Swizzling都會(huì)放在load方法中執(zhí)行,這樣在執(zhí)行main函數(shù)前,就可以對(duì)類方法進(jìn)行交換??梢源_保正式執(zhí)行代碼時(shí),方法肯定是被交換過的。

如果對(duì)一個(gè)類添加Category后,并且重寫其原有方法,這樣會(huì)導(dǎo)致Category中的方法覆蓋原類的方法。但是load方法卻是例外,所有Category和原類的load方法都會(huì)被執(zhí)行。

源碼分析

load方法由Runtime進(jìn)行調(diào)用,下面我們分析一下load方法的實(shí)現(xiàn),load的實(shí)現(xiàn)源碼都在objc-loadmethod.mm中。

在一個(gè)新的工程中,我們創(chuàng)建一個(gè)TestObject類,并在其load方法中打一個(gè)斷點(diǎn),看一下系統(tǒng)的調(diào)用堆棧。

調(diào)用堆棧

從調(diào)用??梢钥闯?,是通過系統(tǒng)的動(dòng)態(tài)鏈接器dyld開始的調(diào)用,然后調(diào)到Runtimeload_images函數(shù)中。load_images函數(shù)是通過_dyld_objc_notify_register函數(shù),將自己的函數(shù)指針注冊(cè)給dyld的。

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

load_images函數(shù)中主要做了兩件事,首先通過prepare_load_methods函數(shù)準(zhǔn)備Class load listCategory load list,然后通過call_load_methods函數(shù)調(diào)用已經(jīng)準(zhǔn)備好的兩個(gè)方法列表。

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!hasLoadMethods((const headerType *)mh)) return;
    prepare_load_methods((const headerType *)mh);
    call_load_methods();
}

首先我們看一下prepare_load_methods函數(shù)的實(shí)現(xiàn),看一下其內(nèi)部是怎么查找load方法的??梢钥吹剑鋬?nèi)部主要分為兩部分,查找Classload方法列表和查找Category方法列表。

準(zhǔn)備類的方法列表時(shí),首先通過_getObjc2NonlazyClassList函數(shù)獲取所有非懶加載類的列表,這時(shí)候獲取到的是一個(gè)classref_t類型的數(shù)組,然后遍歷數(shù)組添加load方法列表。

Category過程也是類似,通過_getObjc2NonlazyCategoryList函數(shù)獲取所有非懶加載Category的列表,得到一個(gè)category_t類型的數(shù)組,需要注意的是這是一個(gè)指向指針的指針。然后對(duì)其進(jìn)行遍歷并添加到load方法列表,ClassCategoryload方法列表是兩個(gè)列表。

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    // 獲取到非懶加載的類的列表
    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        // 設(shè)置Class的調(diào)用列表
        schedule_class_load(remapClass(classlist[i]));
    }
    
    // 獲取到非懶加載的Category列表
    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        // 忽略弱鏈接的類別
        if (!cls) continue;
        // 實(shí)例化所屬的類
        realizeClass(cls);
        // 設(shè)置Category的調(diào)用列表
        add_category_to_loadable_list(cat);
    }
}

在添加類的load方法列表時(shí),內(nèi)部會(huì)遞歸遍歷把所有父類的load方法都添加進(jìn)去,順著繼承者鏈的順序添加,會(huì)先把父類添加在前面。然后會(huì)調(diào)用add_class_to_loadable_list函數(shù),將自己的load方法添加到對(duì)應(yīng)的數(shù)組中。

static void schedule_class_load(Class cls)
{
    if (!cls) return;
    // 已經(jīng)添加Class的load方法到調(diào)用列表中
    if (cls->data()->flags & RW_LOADED) return;

    // 確保super已經(jīng)被添加到load列表中,默認(rèn)是整個(gè)繼承者鏈的順序
    schedule_class_load(cls->superclass);
    
    // 將IMP和Class添加到調(diào)用列表
    add_class_to_loadable_list(cls);
    // 設(shè)置Class的flags,表示已經(jīng)添加Class到調(diào)用列表中
    cls->setInfo(RW_LOADED); 
}

Category則不需要考慮父類的問題,所以直接在prepare_load_methods函數(shù)中遍歷Category數(shù)組,然后調(diào)用add_category_to_loadable_list函數(shù)即可。

add_category_to_loadable_list函數(shù)中,會(huì)判斷當(dāng)前Category有沒有實(shí)現(xiàn)load方法,如果沒有則直接return,如果實(shí)現(xiàn)了則添加到loadable_categories數(shù)組中。

類的add_class_to_loadable_list函數(shù)內(nèi)部實(shí)現(xiàn)也是類似,區(qū)別在于類的數(shù)組叫做loadable_classes。

void add_category_to_loadable_list(Category cat)
{
    IMP method;

    // 獲取Category的load方法的IMP
    method = _category_getLoadMethod(cat);

    // 如果Category沒有l(wèi)oad方法則return
    if (!method) return;
    // 如果已使用大小等于數(shù)組大小,對(duì)數(shù)組進(jìn)行動(dòng)態(tài)擴(kuò)容
    if (loadable_categories_used == loadable_categories_allocated) {
        loadable_categories_allocated = loadable_categories_allocated*2 + 16;
        loadable_categories = (struct loadable_category *)
            realloc(loadable_categories,
                              loadable_categories_allocated *
                              sizeof(struct loadable_category));
    }

    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used++;
}

到此為止,loadable_classesloadable_categories兩個(gè)數(shù)組已經(jīng)準(zhǔn)備好了,load_images會(huì)調(diào)用call_load_methods函數(shù)執(zhí)行這些load方法。在這個(gè)方法中,call_class_loads函數(shù)是負(fù)責(zé)調(diào)用類方法列表的,call_category_loads負(fù)責(zé)調(diào)用Category的方法列表。

void call_load_methods(void)
{
    bool more_categories;
    void *pool = objc_autoreleasePoolPush();

    do {
        // 反復(fù)執(zhí)行call_class_loads函數(shù),直到數(shù)組中沒有可執(zhí)行的load方法
        while (loadable_classes_used > 0) {
            call_class_loads();
        }
        more_categories = call_category_loads();
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);
    loading = NO;
}

下面是調(diào)用類方法列表的代碼,內(nèi)部主要是通過對(duì)loadable_classes數(shù)組進(jìn)行遍歷,并獲取到loadable_class的結(jié)構(gòu)體,結(jié)構(gòu)體中存在ClassIMP,然后直接調(diào)用即可。

Category的調(diào)用方式和類的一樣,就不在下面貼代碼了。需要注意的是,load方法都是直接調(diào)用的,并沒有走運(yùn)行時(shí)的objc_msgSend函數(shù)。

static void call_class_loads(void)
{
    int i;
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 
        (*load_method)(cls, SEL_load);
    }
    
    if (classes) free(classes);
}

struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};

根據(jù)上面的源碼分析,我們可以看出load方法的調(diào)用順序應(yīng)該是父類 -> 子類 -> 分類的順序。因?yàn)閳?zhí)行加載Class的時(shí)機(jī)是在Category之前的,而且load子類之前會(huì)先load父類,所以是這種順序。

initialize

load方法類似的也有initialize方法,initialize方法也是由Runtime進(jìn)行調(diào)用的,自己不可以直接調(diào)用。與load方法不同的是,initialize方法是在第一次調(diào)用類所屬的方法時(shí),才會(huì)調(diào)用initialize方法,而load方法是在main函數(shù)之前就全部調(diào)用了。所以理論上來說initialize可能永遠(yuǎn)都不會(huì)執(zhí)行,如果當(dāng)前類的方法永遠(yuǎn)不被調(diào)用的話。

下面我們研究一下initializeRuntime中的源碼。

在向?qū)ο蟀l(fā)送消息時(shí),lookUpImpOrForward函數(shù)中會(huì)判斷當(dāng)前類是否被初始化,如果沒有被初始化,則先進(jìn)行初始化再調(diào)用類的方法。

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver);

// ....省略好多代碼

// 第一次調(diào)用當(dāng)前類的話,執(zhí)行initialize的代碼
if (initialize  &&  !cls->isInitialized()) {
    _class_initialize (_class_getNonMetaClass(cls, inst));
}

// ....省略好多代碼

在進(jìn)行初始化的時(shí)候,和load方法的調(diào)用順序一樣,會(huì)按照繼承者鏈先初始化父類。_class_initialize函數(shù)中關(guān)鍵的兩行代碼是callInitializelockAndFinishInitializing的調(diào)用。

// 第一次調(diào)用類的方法,初始化類對(duì)象
void _class_initialize(Class cls)
{
    Class supercls;
    bool reallyInitialize = NO;

    // 遞歸初始化父類。initizlize不用顯式的調(diào)用super,因?yàn)閞untime已經(jīng)在內(nèi)部調(diào)用了
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }
    
    {
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;
        }
    }
    
    if (reallyInitialize) {
        _setThisThreadIsInitializingClass(cls);

        if (MultithreadedForkChild) {
            performForkChildInitialize(cls, supercls);
            return;
        }
        @try {
            // 通過objc_msgSend()函數(shù)調(diào)用initialize方法
            callInitialize(cls);
        }
        @catch (...) {
            @throw;
        }
        @finally {
            // 執(zhí)行initialize方法后,進(jìn)行系統(tǒng)的initialize過程
            lockAndFinishInitializing(cls, supercls);
        }
        return;
    }
    
    else if (cls->isInitializing()) {
        if (_thisThreadIsInitializingClass(cls)) {
            return;
        } else if (!MultithreadedForkChild) {
            waitForInitializeToComplete(cls);
            return;
        } else {
            _setThisThreadIsInitializingClass(cls);
            performForkChildInitialize(cls, supercls);
        }
    }
}

通過objc_msgSend函數(shù)調(diào)用initialize方法。

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}

lockAndFinishInitializing函數(shù)中會(huì)完成一些初始化操作,其內(nèi)部會(huì)調(diào)用_finishInitializing函數(shù),在函數(shù)內(nèi)部會(huì)調(diào)用classsetInitialized函數(shù),核心工作都由setInitialized函數(shù)完成。

static void lockAndFinishInitializing(Class cls, Class supercls)
{
    monitor_locker_t lock(classInitLock);
    if (!supercls  ||  supercls->isInitialized()) {
        _finishInitializing(cls, supercls);
    } else {
        _finishInitializingAfter(cls, supercls);
    }
}

負(fù)責(zé)初始化類和元類,函數(shù)內(nèi)部主要是查找當(dāng)前類和元類中是否定義了某些方法,然后根據(jù)查找結(jié)果設(shè)置類和元類的一些標(biāo)志位。

void 
objc_class::setInitialized()
{
    Class metacls;
    Class cls;

    // 獲取類和元類對(duì)象
    cls = (Class)this;
    metacls = cls->ISA();

    bool inherited;
    bool metaCustomAWZ = NO;
    if (MetaclassNSObjectAWZSwizzled) {
        metaCustomAWZ = YES;
        inherited = NO;
    }
    else if (metacls == classNSObject()->ISA()) {
        // 查找是否實(shí)現(xiàn)了alloc和allocWithZone方法
        auto& methods = metacls->data()->methods;
        for (auto mlists = methods.beginCategoryMethodLists(), 
                  end = methods.endCategoryMethodLists(metacls); 
             mlists != end;
             ++mlists)
        {
            if (methodListImplementsAWZ(*mlists)) {
                metaCustomAWZ = YES;
                inherited = NO;
                break;
            }
        }
    }
    else if (metacls->superclass->hasCustomAWZ()) {
        metaCustomAWZ = YES;
        inherited = YES;
    } 
    else {
        auto& methods = metacls->data()->methods;
        for (auto mlists = methods.beginLists(),
                  end = methods.endLists(); 
             mlists != end;
             ++mlists)
        {
            if (methodListImplementsAWZ(*mlists)) {
                metaCustomAWZ = YES;
                inherited = NO;
                break;
            }
        }
    }
    if (!metaCustomAWZ) metacls->setHasDefaultAWZ();
    if (PrintCustomAWZ  &&  metaCustomAWZ) metacls->printCustomAWZ(inherited);

    bool clsCustomRR = NO;
    if (ClassNSObjectRRSwizzled) {
        clsCustomRR = YES;
        inherited = NO;
    }
    // 查找元類是否實(shí)現(xiàn)MRC方法,如果是則進(jìn)入if語句中
    if (cls == classNSObject()) {
        auto& methods = cls->data()->methods;
        for (auto mlists = methods.beginCategoryMethodLists(), 
                  end = methods.endCategoryMethodLists(cls); 
             mlists != end;
             ++mlists)
        {
            if (methodListImplementsRR(*mlists)) {
                clsCustomRR = YES;
                inherited = NO;
                break;
            }
        }
    }
    else if (!cls->superclass) {
        clsCustomRR = YES;
        inherited = NO;
    } 
    else if (cls->superclass->hasCustomRR()) {
        clsCustomRR = YES;
        inherited = YES;
    } 
    else {
        // 查找類是否實(shí)現(xiàn)MRC方法,如果是則進(jìn)入if語句中
        auto& methods = cls->data()->methods;
        for (auto mlists = methods.beginLists(), 
                  end = methods.endLists(); 
             mlists != end;
             ++mlists)
        {
            if (methodListImplementsRR(*mlists)) {
                clsCustomRR = YES;
                inherited = NO;
                break;
            }
        }
    }
    if (!clsCustomRR) cls->setHasDefaultRR();
    if (PrintCustomRR  &&  clsCustomRR) cls->printCustomRR(inherited);
    metacls->changeInfo(RW_INITIALIZED, RW_INITIALIZING);
}

需要注意的是,initialize方法和load方法不太一樣,Category中定義的initialize方法會(huì)覆蓋原方法而不是像load方法一樣都可以調(diào)用。


簡書由于排版的問題,閱讀體驗(yàn)并不好,布局、圖片顯示、代碼等很多問題。所以建議到我Github上,下載Runtime PDF合集。把所有Runtime文章總計(jì)九篇,都寫在這個(gè)PDF中,而且左側(cè)有目錄,方便閱讀。

Runtime PDF

下載地址:Runtime PDF
麻煩各位大佬點(diǎn)個(gè)贊,謝謝!??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容