_objc_init()入手 學習Runtime

_objc_init() 實現細節

前言

Runtime做爲Objective-C的運行時的核心一直是iOS開發者必需要學習瞭解的,從事iOS開發也有一段時間,以前都是斷斷續續看Runtime的源碼或者是經過別人的博客思路來學習瞭解Runtime。從今年三月份開始系統的研讀了Runtime的源碼、發現大體和以前瞭解的相同(網上各路大神太多了,攤手臉!),恰好最近項目不忙,因此決定寫幾篇博客記錄下這段經歷,LET'S GO。git

經過翻碼發現蘋果在庫初始化以前由dyld程序註冊了三個通知程序,來實現整個RunTime系統的組建、+load的調用、以及資源釋放。github

_objc_init源碼實現以下:swift

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    //Alex註釋: 讀取Runtime相關的環境變量
    environ_init();
    tls_init();
    static_init();
    lock_init();
    //Alex註釋: 初始化libobjc異常處理系統
    exception_init();
    /* Alex註釋: 重要的來了,註冊通知程序:這裏經過名字咱們能夠猜想出蘋果經過dyld註冊了
     * 三個程序分別是map_images、load_images、unmap_image經過名字能夠大概猜想出他們
     * 的做用、接下來讓咱們來揭開這層面紗。
     * ***************這裏比較遺憾的是沒有找到_dyld_objc_notify_register實現的源碼
     * ***************若是有大神知道歡迎評論指出
     */
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
複製代碼

dyld源碼 感興趣的能夠去了解。數組

1、map_images方法的實現分析

蘋果對於map_images的註釋以下: Process the given images which are being mapped in by dyld. Calls ABI-agnostic code after taking ABI-specific locks. Google翻譯以後大體意思是處理由dyld映射的給定images,獲取ABI鎖並調用與ABI無關的代碼。 images:網上不少翻譯爲鏡像,可是我看源碼則更像是資源文件解析。緩存

map_images的源碼實現以下:安全

void map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    //Alex註釋: 開啓Runtime鎖
    mutex_locker_t lock(runtimeLock);
    //Alex註釋: 將具體任務交由map_images_nolock執行
    return map_images_nolock(count, paths, mhdrs);
}
複製代碼

map_images將具體的處理交由map_images_nolock方法執行,map_images_nolock實現大概150行代碼,其主要作了下面這些事情:bash

  • 一、獲取共享緩存的內存區域。
  • 二、將dyld中讀取的mach_header *mhdrs[]轉化爲header_info *hList[]結構體數組。
  • 三、將轉化出的header_info添加到FirstHeader的結構體鏈表中。
  • 四、初始化靜態的全局變量namedSelectors(NXMapTable類型),並註冊咱們所熟知的一些方法 如load、initialize、retain、alloc、dealloc、copy。
  • 五、初始化AutoreleasePool,靜態全局變量static uint8_t SideTableBuf [sizeof(StripedMap)]。
  • 六、最後將轉化好的header_info結構體數組交給_read_images進行更細緻的解析。

map_images_nolock源碼(有精簡)實現以下:app

void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
    static bool firstTime = YES;
    //Alex註釋: 初始化header_info結構體數組
    header_info *hList[mhCount];
    //Alex註釋: 用於記錄hList的大小
    uint32_t hCount;
    //Alex註釋: 用於記錄方法和消息的個數
    size_t selrefCount = 0;

    if (firstTime) {
        //Alex註釋: 獲取共享緩存的內存區域
        preopt_init();
    }
    hCount = 0;

    int totalClasses = 0;
    int unoptimizedTotalClasses = 0;
    {
        uint32_t i = mhCount;
        while (i--) {
            const headerType *mhdr = (const headerType *)mhdrs[i];
            /*Alex註釋:
             * 將mhdr 轉化爲 header_info 添加到 FirstHeader 鏈表中;若是添加成功,addHeader 返回 header_info 結構體 else NULL。
             */
            auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
            if (!hi) {
                continue;
            }
            
            if (mhdr->filetype == MH_EXECUTE) {
             /*Alex註釋:
             * 獲取 header_info中的sel數量,這裏區分了__OBJC2__和其餘版本、是由於OBJC
             * 2以後提出了消息概念、將以前的SEL變爲了message_ref_t { IMP imp;  SEL 
             * sel;};的結構體封裝,而這正是後來黑魔法實現的基本原理。
             */
#if __OBJC2__
                size_t count;
                _getObjc2SelectorRefs(hi, &count);
                selrefCount += count;
                _getObjc2MessageRefs(hi, &count);
                selrefCount += count;
#else
                _getObjcSelectorRefs(hi, &selrefCount);
#endif
            }
            hList[hCount++] = hi;
        }
    }

    if (firstTime) {
        /*Alex註釋:
         * 初始化全局的namedSelectors(NXMapTable類型)變量,並註冊必要的的方法如 load、initialize、retain、release、autorelease、retainCount等...
         */
        sel_init(selrefCount);
        /*Alex註釋:
         * 初始化自動釋放池AutoreleasePool(AutoreleasePoolPage)、全局SideTableBuf變量(StripedMap<SideTable>)
         * SideTableBuf是Runtime的核心、
           關於SideTableBuf本人的其餘博客有介紹歡迎翻閱。
         */
        arr_init();
    }

    if (hCount > 0) {
        /*Alex註釋:
         * 通過以上處理將mach_header結構體數據轉化爲了header_info結構體數據,接着蘋果將header_info的處理轉交給了_read_images方法去實現。
         */
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
    firstTime = NO;
}
複製代碼

蘋果對於_read_images的註釋以下: Perform initial processing of the headers in the linked list beginning with headerList. Called by: map_images_nolock Google翻譯以後大意爲: 對連接中的header_info列表執行初始化處理,由map_images_nolock調用ide

_read_images主要作了以下這些事情:佈局

  • 一、初始化指針代碼混淆器。
  • 二、初始化全局變量gdb_objc_realized_classes(NXMapTable),保存不在共享緩存中的命名類, 不管是否實現。
  • 三、初始化靜態全局變量allocatedClasses(NXMapTable),緩存已經被objc_allocateClassPair 初始化的類和元類。
  • 四、從header_info中讀取classref_t列表,若是類未被初始化、從future_named_class_map讀取 類進行初始化,若是是非元類將其保存到靜態全局變量nonmeta_class_map中,若是是元類將其保存到全局變量gdb_objc_realized_classes中,最後將初始化的類或者元類保存到allocatedClasses全局Map中。
  • 五、讀取header_info中的SEL並保存到靜態的全局變量namedSelectors中。
  • 六、讀取header_info中的protocol並保存到靜態變量protocol_map中。

接下來經過源碼逐行來了解蘋果是如何處理header_info列表的,

_read_images(有精簡)源碼實現以下:

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;
    runtimeLock.assertLocked();

    //Alex註釋:首次加載初始化
    if (!doneOnce) {
        doneOnce = YES;

    //Alex註釋:ISA適配
#if SUPPORT_NONPOINTER_ISA

# if SUPPORT_INDEXED_ISA
        //Alex註釋: SwiftVersion3以前的版本不支持NonpointerIsa
        for (EACH_HEADER) {
            if (hi->info()->containsSwift()  &&
                hi->info()->swiftVersion() < objc_image_info::SwiftVersion3)
            {
                DisableNonpointerIsa = true;
                break;
            }
        }
# endif

# if TARGET_OS_OSX
        //Alex註釋: OS X 10.11以前的版本不支持NonpointerIsa
        if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {
            DisableNonpointerIsa = true;
        }
        //Alex註釋: 有__DATA,__objc_rawisa段的不支持NonpointerIsa
        for (EACH_HEADER) {
            if (hi->mhdr()->filetype != MH_EXECUTE) continue;
            unsigned long size;
            if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
                DisableNonpointerIsa = true;
            }
            break;  
        }
# endif

#endif
        //Alex註釋: 是否禁用TaggedPointers
        if (DisableTaggedPointers) {
            disableTaggedPointers();
        }
        //Alex註釋: 初始化混淆器(隨機產生),保護代碼安全。
        initializeTaggedPointerObfuscator();

        int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        /*Alex註釋:
         * gdb_objc_realized_classes 保存不在動態共享緩存(dyld shared cache)中的 命名類  -> hash表
         * allocatedClasses 緩存已經被objc_allocateClassPair方法初始化的類和元類  -> hash表
         */
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
        allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
    }

    for (EACH_HEADER) {
        //Alex註釋: 加載每一個header_info結構體的Classref_t結構體列表
        classref_t *classlist = _getObjc2ClassList(hi, &count);
        
        if (! mustReadClasses(hi)) {
            continue;
        }

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

        for (i = 0; i < count; i++) {
            Class cls = (Class)classlist[i];
               /*Alex註釋:
                * readClass主要工做以下:
                * 一、從future_named_class_map(NXMapTable)
                *    全局對象中查找未實現的newCls,取出該Class的(class_rw_t)data數據
                *    (rwNew),將cls數據拷貝到newCls,將拷貝後的newCls的
                *    (class_rw_t)data強轉爲class_ro_t並賦值到rwNew的ro成員變量,
                *    最後將rwNew賦值給newCls的(class_rw_t)data
                * 二、將newCls添加到全局變量(NXMapTable)gdb_objc_realized_classes的
                *    MapTable中,gdb_objc_realized_classes保存不在動態共享緩存中的類
                *    不管是否實現
                * 三、將newCls添加到全局變量(NXMapTable)allocatedClasses的
                *    MapTable中,allocatedClasses表保存已經Allocated的類
                */
            Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);

            if (newCls != cls  &&  newCls) {
                //Alex註釋: newCls添加進數組
                resolvedFutureClasses = (Class *)
                    realloc(resolvedFutureClasses, 
                            (resolvedFutureClassCount+1) * sizeof(Class));
                resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
            }
        }
    }
*******************************須要仔細閱讀源碼
    //Alex註釋: noClassesRemapped建立remapped_class_map靜態變量(NXMapTable),
    if (!noClassesRemapped()) {
        for (EACH_HEADER) {
            Class *classrefs = _getObjc2ClassRefs(hi, &count);
            for (i = 0; i < count; i++) {
                //Alex註釋: 返回實時的類指針,該指針可能指向已經從新分配內存的類結構
                remapClassRef(&classrefs[i]);
            }
            classrefs = _getObjc2SuperRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapClassRef(&classrefs[i]);
            }
        }
    }
    //Alex註釋: 將SEL保存到全局的namedSelectors(NXMapTable表)中
    static size_t UnfixedSelectors;
    {
        mutex_locker_t lock(selLock);
        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]);
                sels[i] = sel_registerNameNoLock(name, isBundle);
            }
        }
    }

    //Alex註釋: 加載protocols,並保存到靜態變量(NXMapTable)protocol_map中
    for (EACH_HEADER) {
        extern objc_class OBJC_CLASS_$_Protocol;
        Class cls = (Class)&OBJC_CLASS_$_Protocol;
        assert(cls);
        NXMapTable *protocol_map = protocols();
        bool isPreoptimized = hi->isPreoptimized();
        bool isBundle = hi->isBundle();

        protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
        for (i = 0; i < count; i++) {
            readProtocol(protolist[i], cls, protocol_map, 
                         isPreoptimized, isBundle);
        }
    }

    //Alex註釋: 加載ProtocolRefs,並保存到靜態變量(NXMapTable)protocol_map中
    for (EACH_HEADER) {
        protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapProtocolRef(&protolist[i]);
        }
    }

    //Alex註釋: 加載非惰性類,用於+load和靜態的instance
    for (EACH_HEADER) {
        classref_t *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;

#if TARGET_OS_SIMULATOR
            if (cls->cache._buckets == (void*)&_objc_empty_cache  &&  
                (cls->cache._mask  ||  cls->cache._occupied)) 
            {
                cls->cache._mask = 0;
                cls->cache._occupied = 0;
            }
            if (cls->ISA()->cache._buckets == (void*)&_objc_empty_cache  &&  
                (cls->ISA()->cache._mask  ||  cls->ISA()->cache._occupied)) 
            {
                cls->ISA()->cache._mask = 0;
                cls->ISA()->cache._occupied = 0;
            }
#endif
*******************************敲重點:::須要仔細閱讀源碼
            //Alex註釋:將cls添加到allocatedClasses全局map中
            addClassTableEntry(cls);
            /* Alex註釋: 加載非惰性類、執行初始化
            *  遞歸佈局class、superClass、metaClass的樹形結構
            *  reconcileInstanceVariables方法協調實例變量的偏移量,內存佈局
            */
            realizeClass(cls);
        }
    }
    //Alex註釋:實現新的未解決的將來類?????????
    if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            realizeClass(resolvedFutureClasses[i]);
            resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
        }
        free(resolvedFutureClasses);
    }    

    //Alex註釋:加載類別(categories)列表
    for (EACH_HEADER) {
        category_t **catlist = 
            _getObjc2CategoryList(hi, &count);
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();

        for (i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);

            if (!cls) {
                catlist[i] = nil;
                continue;
            }

            bool classExists = NO;
            //Alex註釋:判斷是否有類的實例方法、協議、實例屬性
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                //Alex註釋:category存放在靜態的MapTable(靜態變量category_map)中,
                 * 蘋果是將category和header_info封裝成locstamped_category_t結構體, 
                 * 以後將(locstamped_category_t){category, header_info} 添加到全局
                 * 變量category_map中category->cls映射的locstamped_category_t結構
                 * 體數組中
                 */
                addUnattachedCategoryForClass(cat, cls, hi);
                if (cls->isRealized()) {
                    //Alex註釋: 若是類已經初始化將category的方法、屬性、協議追加到類的
                    * class_rw_t結構體對應的methods、properties、protocols成員
                    * 變量中
                    * 添加到class_rw_t結構體的操做是在attachCategories方法中實現的。
                    */
                    remethodizeClass(cls);
                    classExists = YES;
                }
            }
            //Alex註釋:這裏處理的是元類的方法、協議、屬性
            if (cat->classMethods  ||  cat->protocols  
                ||  (hasClassProperties && cat->_classProperties)) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
            }
        }
    }
    //Alex註釋:調試不易碎的Ivars
    if (DebugNonFragileIvars) {
        //Alex註釋:非惰性的實現全部已知的未實現的類
        realizeAllClasses();
    }

    //Alex註釋:打印預選的方法列表、優化方法列表、所有的Classes、優化的Classes, DEBUG
    if (PrintPreopt) {
        static unsigned int PreoptTotalMethodLists;
        static unsigned int PreoptOptimizedMethodLists;
        static unsigned int PreoptTotalClasses;
        static unsigned int PreoptOptimizedClasses;

        for (EACH_HEADER) {
            classref_t *classlist = _getObjc2ClassList(hi, &count);
            for (i = 0; i < count; i++) {
                Class cls = remapClass(classlist[i]);
                if (!cls) continue;

                PreoptTotalClasses++;
                if (hi->isPreoptimized()) {
                    PreoptOptimizedClasses++;
                }
                
                const method_list_t *mlist;
                if ((mlist = ((class_ro_t *)cls->data())->baseMethods())) {
                    PreoptTotalMethodLists++;
                    if (mlist->isFixedUp()) {
                        PreoptOptimizedMethodLists++;
                    }
                }
                if ((mlist=((class_ro_t *)cls->ISA()->data())->baseMethods())) {
                    PreoptTotalMethodLists++;
                    if (mlist->isFixedUp()) {
                        PreoptOptimizedMethodLists++;
                    }
                }
            }
        }
    }

#undef EACH_HEADER
}

複製代碼

通過以上處理類的裝載已經完成、而後執行load_images方法、處理已經被加載的類的+load方法。

2、分析load_images實現

蘋果對於load_images的註釋以下: Process +load in the given images which are being mapped in by dyld. Google翻譯後大意是:處理在dyld被mapped的images的+load方法。

load_images的代碼並很少,主要是對load_images執行遞歸鎖操做,以後調用prepare_load_methods裝載+load方法,最後經過call_load_methods調用Class和Category的+load方法。

void load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!hasLoadMethods((const headerType *)mh)) return;
    //Alex註釋: loadMethod加鎖
    recursive_mutex_locker_t lock(loadMethodLock);
    {
        mutex_locker_t lock2(runtimeLock);
        //Alex註釋: 裝載+load方法
        prepare_load_methods((const headerType *)mh);
    }
    //Alex註釋: 調用+load方法
    call_load_methods();
}
複製代碼

咱們知道category和class均可以實現+load方法,下面經過源碼來細究Category和Class的+load方法是如何實現的。

prepare_load_methods方法實現比較簡單、主要是對mhdr中的Class和Category進行遍歷、最後將+load的方法處理分發到了schedule_class_load和add_category_to_loadable_list方法、分別用於裝載Class的+load和Category的+load方法。

prepare_load_methods源碼實現以下:

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

    classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        /*Alex註釋:
         * 加載類的 +load方法,並將Class的load方法和類保存到全局的
         * (struct loadable_class *)loadable_classes 結構體數組中
         */
        schedule_class_load(remapClass(classlist[i]));
    }

    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; 
        realizeClass(cls);
        assert(cls->ISA()->isRealized());
        /*Alex註釋:
         * 加載category的 +load方法, 並將category的load方法和category存儲到 
         * (struct loadable_category *)loadable_categories 結構體數組中
         */
        add_category_to_loadable_list(cat);
    }
}
複製代碼

關於類的+load方法的裝載、經過源碼能夠看出蘋果是採用遞歸的方式從父類到子類逐個遍歷,最後將具體的裝載處理交由add_class_to_loadable_list方法執行。

add_class_to_loadable_list實現就更加簡明瞭,大體分爲下面幾步:

  • 一、獲取Class的+load方法
  • 二、確保struct loadable_class *loadable_classes結構體數組有足夠空間
  • 三、將Class和+load方法保存到loadable_classes結構體數組中
static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());

    if (cls->data()->flags & RW_LOADED) return;

    schedule_class_load(cls->superclass);
    add_class_to_loadable_list(cls);
   <!-- add_class_to_loadable_list 實現 {
        IMP method;

        loadMethodLock.assertLocked();

        method = cls->getLoadMethod();
        if (!method) return; 
        
        if (loadable_classes_used == loadable_classes_allocated) {
            loadable_classes_allocated = loadable_classes_allocated*2 + 16;
            loadable_classes = (struct loadable_class *)
                realloc(loadable_classes,
                                  loadable_classes_allocated *
                                  sizeof(struct loadable_class));
        }
        
        loadable_classes[loadable_classes_used].cls = cls;
        loadable_classes[loadable_classes_used].method = method;
        loadable_classes_used++;
    } -->
    cls->setInfo(RW_LOADED); 
}
複製代碼

關於Category的+load方法的裝載,其實和類的裝載大體相同。只是這裏在調用add_category_to_loadable_list方法裝載時、若是Class沒有realize的話會對Class進行realize,還要注意的是Category的+load方法是被單獨保存在一個全局的struct loadable_category *loadable_categories 靜態變量中的,他和類的+load方法是分開存放的。

+load方法裝載以後蘋果經過call_load_methods方法來調用Class和Category的+load方法。Class和Category的+load調用順序在這裏也能夠獲得想要的答案。

call_load_methods的實現源碼以下:

void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        //Alex註釋: 循環調用Class的+load方法
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        //Alex註釋:調用category的+load方法,僅調用一次
        more_categories = call_category_loads();

    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}
複製代碼

類的+load的調用源碼:

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;
    //Alex註釋:遍歷調用Class的+load方法
    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; 
        //Alex註釋:調用+load方法
        (*load_method)(cls, SEL_load);
    }
    if (classes) free(classes);
}
複製代碼

Category的+load方法的調用源碼:

static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    
    struct loadable_category *cats = loadable_categories;
    int used = loadable_categories_used;
    int allocated = loadable_categories_allocated;
    loadable_categories = nil;
    loadable_categories_allocated = 0;
    loadable_categories_used = 0;

    //Alex註釋: 循環調用Category的+load方法
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if (!cat) continue;

        cls = _category_getClass(cat);
        if (cls  &&  cls->isLoadable()) {
            //Alex註釋:調用+load方法
            (*load_method)(cls, SEL_load);
            cats[i].cat = nil;
        }
    }

    shift = 0;
    for (i = 0; i < used; i++) {
        if (cats[i].cat) {
            cats[i-shift] = cats[i];
        } else {
            shift++;
        }
    }
    used -= shift;

    new_categories_added = (loadable_categories_used > 0);
    for (i = 0; i < loadable_categories_used; i++) {
        if (used == allocated) {
            allocated = allocated*2 + 16;
            cats = (struct loadable_category *)
                realloc(cats, allocated *
                                  sizeof(struct loadable_category));
        }
        cats[used++] = loadable_categories[i];
    }

    if (loadable_categories) free(loadable_categories);

    if (used) {
        loadable_categories = cats;
        loadable_categories_used = used;
        loadable_categories_allocated = allocated;
    } else {
        if (cats) free(cats);
        loadable_categories = nil;
        loadable_categories_used = 0;
        loadable_categories_allocated = 0;
    }

    return new_categories_added;
}
複製代碼

3、分析unmap_image實現

unmap_image對應map_images有map操做就有對應的unmap。 蘋果對於unmap的註釋以下: Process the given image which is about to be unmapped by dyld. Google翻譯大意爲:處理由dyld取消映射的image。

unmap_image的源碼以下:
void  unmap_image(const char *path __unused, const struct mach_header *mh)
{
    recursive_mutex_locker_t lock(loadMethodLock);
    mutex_locker_t lock2(runtimeLock);
    //Alex註釋: 加鎖將具體的任務轉交unmap_image_nolock方法
    unmap_image_nolock(mh);
}
複製代碼

從全局的header_info結構體鏈表中遍歷查找Runtime的header_info結構體,調用_unload_image處理header_info結構體、最後將其從鏈表中移除。

void unmap_image_nolock(const struct mach_header *mh)
{
    header_info *hi;
    
    for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
        if (hi->mhdr() == (const headerType *)mh) {
            break;
        }
    }

    if (!hi) return;

    _unload_image(hi);

    removeHeader(hi);
    free(hi);
}
複製代碼
void _unload_image(header_info *hi)
{
    size_t count, i;

    loadMethodLock.assertLocked();
    runtimeLock.assertLocked();
    //Alex註釋: 中止附加Category和調用Category的+load方法
    category_t **catlist = _getObjc2CategoryList(hi, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = catlist[i];
        if (!cat) continue; 
        Class cls = remapClass(cat->cls);
        assert(cls); 

        removeUnattachedCategoryForClass(cat, cls);

        remove_category_from_loadable_list(cat);
    }

    NXHashTable *classes = NXCreateHashTable(NXPtrPrototype, 0, nil);
    classref_t *classlist;
    //Alex註釋: 從header_info結構體中加載已經remap的Classs
    classlist = _getObjc2ClassList(hi, &count);
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        if (cls) NXHashInsert(classes, cls);
    }

    classlist = _getObjc2NonlazyClassList(hi, &count);
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        if (cls) NXHashInsert(classes, cls);
    }

    NXHashState hs;
    Class cls;

    hs = NXInitHashState(classes);
    while (NXNextHashState(classes, &hs, (void**)&cls)) {
        //Alex註釋: 將loadable表中的類移除
        remove_class_from_loadable_list(cls);
        //Alex註釋: 將類、元類從已經初始化的全局靜態表中移除
        detach_class(cls->ISA(), YES);
        detach_class(cls, NO);
    }
    hs = NXInitHashState(classes);
    while (NXNextHashState(classes, &hs, (void**)&cls)) {
        //Alex註釋: 釋放類和元類指針
        free_class(cls->ISA());
        free_class(cls);
    }
    //Alex註釋: 釋放hashTable
    NXFreeHashTable(classes);
}
複製代碼

4、總結

此次源碼閱讀學習到了類是如何從共享緩存、讀取、轉化、以及初始化這樣一個裝載過程,學到了類、方法、協議等在內存中存儲的形式以及類、元類、超類之間的關係。在load_images模塊也證明了以前關於category和class中+load方法的認知。 將來可期,加油少年!!

相關文章
相關標籤/搜索