iOS 從 _objc_init 分析類的加載流程

1、前言

在啓動 app 的時候, dyld 會對動態庫進行加載、連接等一系列動做,以後就會來到 libobjc.A.dylib 庫中調用 _objc_init 對類進行處理,經過 map_images 映射出整個鏡像文件,再經過 read_images 加載鏡像文件,此時類已經加載完成,那其中類的加載的流程又是怎麼樣的呢?類的屬性、方法、協議都是怎麼加載的呢?接下來就從 _objc_init 開始整篇文章的分析。附上 objc 源碼 下載連接。c++

2、_objc_init 源碼分析

void _objc_init(void)
{
  static bool initialized = false;
  if (initialized) return;
  initialized = true;
  
  // 環境變量的一些操做
  environ_init();
  tls_init();
  // 系統級別的 c++ 構造函數調用
  static_init();
  // 空函數,預留
  lock_init();
  // 註冊監聽異常的回調
  exception_init();
  // 註冊回調通知
  _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
複製代碼

1. environ_init 分析

environ_init 方法是讀取影響運行時的環境變量,最後內部有一段打印環境變量的代碼。我將它提取出來,不作判斷,打印結果以下。固然也能夠在終端使用 export OBJC_HELP=1 指令打印環境變量。swift

咱們能夠在 Xcode 中修改環境變量的值來達到咱們調試的一些目的。

2. environ_init 分析

主要是對線程 key 的綁定。數組

void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
    _objc_pthread_key = TLS_DIRECT_KEY;
    pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
    _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
複製代碼

3. static_init 分析

運行 C++ 靜態構造函數,在 dyld 調用咱們自定義構造函數以前。緩存

static void static_init()
{
    size_t count;
    auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    for (size_t i = 0; i < count; i++) {
        inits[i]();
    }
}
複製代碼

4. lock_init 分析

空函數,猜想可能爲了之後作的預留。bash

void lock_init(void)
{
}
複製代碼

5. exception_init 分析

初始化 libobjc 庫的異常處理系統,註冊監聽異常崩潰的回調,當發生崩潰時,就會來到 _objc_terminate 函數裏面。app

void exception_init(void)
{
    old_terminate = std::set_terminate(&_objc_terminate);
}
static void _objc_terminate(void)
{
    if (PrintExceptions) {
        _objc_inform("EXCEPTIONS: terminating");
    }

    if (! __cxa_current_exception_type()) {
        // No current exception.
        (*old_terminate)();
    }
    else {
        // There is a current exception. Check if it’s an objc exception.
        @try {
            __cxa_rethrow();
        } @catch (id e) {
            // It’s an objc object. Call Foundation’s handler, if any.
            (*uncaught_handler)((id)e);
            (*old_terminate)();
        } @catch (...) {
            // It’s not an objc object. Continue to C++ terminate.
            (*old_terminate)();
        }
    }
}
複製代碼

3、_dyld_objc_notify_register 源碼分析

整個 objc 在這個裏面是一個運行時環境,運行時環境去加載全部類的一些信息的時候,就會依賴這個註冊函數的回調的通知,告訴當前的 dyld 的作了哪些事情,你須要哪些環境來進行彼此的通信,好比當前的 map_images函數

0. 知識預備:懶加載類和非懶加載類的區別

簡單來講就是有沒有實現 load 方法,非懶加載類在類的內部實現了 load 方法,類的加載就會提早,而懶加載類沒有實現 load 方法,在使用的第一次纔會加載,當咱們再給這個類的發送消息,若是是第一次,在消息查找的過程當中就會判斷這個類是否加載,沒有加載就會加載這個類。oop

1. map_images 分析

map_images 方法在 image 加載到內存的時候會觸發該方法的調用。忽略內部函數跳轉、打印和操做 hCount 的代碼,最終會來到 _read_images源碼分析

2. _read_images 分析

(1) _read_images 方法首先建立了兩張表用來存儲類。
// 若是是第一次進來,就會走 if 下面的代碼
    if (!doneOnce) {
        // 以後就不會來了
        // 爲何只來一次呢,由於第一次進來的時候,類,協議,sel,分類都沒有
        // 須要建立容器來保存這些東西,這裏建立的是兩個表。
        doneOnce = YES;
        
        //... 忽略一些可有可無的代碼

        if (DisableTaggedPointers) {
            disableTaggedPointers();
        }
        // TaggedPointer 的優化處理
        initializeTaggedPointerObfuscator();

        // 4/3是 NXMapTable 的負載因子
        int namedClassesSize = 
            (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
        // 實例化存儲類的哈希表,並根據當前類的數量作動態擴容
        // 只要你沒有在共享緩存的類,無論實現或者未實現都會在這個裏面
        gdb_objc_realized_classes =
            NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
        // 已經被分配的類和元類都會放在這個表裏
        allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
    }
複製代碼
(2) readClass 分析
for (EACH_HEADER) {
        // 從編譯後的類列表中取出全部類,獲取到的是一個classref_t類型的指針
        classref_t *classlist = _getObjc2ClassList(hi, &count);
        
        if (! mustReadClasses(hi)) {
            // 圖像充分優化,咱們不須要調用 readClass()
            continue;
        }

        bool headerIsBundle = hi->isBundle();
        bool headerIsPreoptimized = hi->isPreoptimized();
        
        for (i = 0; i < count; i++) {
             // 數組中會取出OS_dispatch_queue_concurrent、OS_xpc_object、NSRunloop等系統類,例如CF、Fundation、libdispatch中的類。以及本身建立的類
            Class cls = (Class)classlist[i];
            
            // 經過 readClass 函數獲取處理後的新類,內部主要操做 ro 和 rw 結構體
            Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);

            // 初始化全部懶加載的類須要的內存空間,並將全部的將來須要處理的類添加到一個數組中 
            // 如今數據沒有加載到的,連類都沒有初始化
            if (newCls != cls  &&  newCls) {
                // Class was moved but not deleted. Currently this occurs 
                // only when the new class resolved a future class.
                // Non-lazily realize the class below.
                // 將懶加載的類添加到數組中
                resolvedFutureClasses = (Class *)
                    realloc(resolvedFutureClasses, 
                            (resolvedFutureClassCount+1) * sizeof(Class));
                resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
            }
        }
}

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->mangledName();
    // 若是某個 cls 的 superclass 是 weak-linked 的而且丟失了,則返回YES。
    if (missingWeakSuperclass(cls)) {
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", 
                         cls->nameForLogging());
        }
        // 添加到重映射表裏面,映射爲 nil
        addRemappedClass(cls, nil);
        cls->superclass = nil;
        return nil;
    }
    
    //... 忽略一些可有可無的代碼

    cls->fixupBackwardDeployingStableSwift();

    Class replacing = nil;
    // 對將來的一些類的 ro 和 rw 的特殊處理,通常不會進去
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // 將objc_class複製到future類的結構中
        // 保存future 類的 rw
        class_rw_t *rw = newCls->data();
        const class_ro_t *old_ro = rw->ro;
        memcpy(newCls, cls, sizeof(objc_class));
        rw->ro = (class_ro_t *)newCls->data();
        newCls->setData(rw);
        freeIfMutable((char *)old_ro->name);
        free((void *)old_ro);
        
        addRemappedClass(cls, newCls);
        replacing = cls;
        cls = newCls;
    }
    
    if (headerIsPreoptimized  &&  !replacing) {
        // 斷言
        assert(getClassExceptSomeSwift(mangledName));
    } else {
        // 將 cls 加入到 gdb_objc_realized_classes 表裏面去
        addNamedClass(cls, mangledName, replacing);
        // 將 cls 插入到 allocatedClasses 表裏面去
        addClassTableEntry(cls);
    }

    // 供之後參考:共享緩存從不包含mh_bundle
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    // 到此時,這個類在整個表裏就有了,返回
    return cls;
}
複製代碼
(3) remapClassRef
// 主要是修復重映射 - !noClassesRemapped() 在這裏爲 false,因此通常走不進來
// 將未映射Class和Super Class重映射,被remap的類都是非懶加載的類
if (!noClassesRemapped()) {
    for (EACH_HEADER) {
        // 重映射Class,注意是從_getObjc2ClassRefs函數中取出類的引用
        Class *classrefs = _getObjc2ClassRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapClassRef(&classrefs[i]);
        }
        // fixme why doesn’t test future1 catch the absence of this?
        classrefs = _getObjc2SuperRefs(hi, &count);
        for (i = 0; i < count; i++) {
            remapClassRef(&classrefs[i]);
        }
    }
}
複製代碼
(4) sel_registerNameNoLock
// 將全部SEL都註冊到哈希表中,是另一張哈希表
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++) {
            // sel_cname 將 SEL 強轉爲 char 類型
            const char *name = sel_cname(sels[i]);
            // 註冊 SEL 的操做
            sels[i] = sel_registerNameNoLock(name, isBundle);
        }
    }
}
複製代碼
(5) fixupMessageRef
// 修復舊的函數指針調用遺留
for (EACH_HEADER) {
    message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
    if (count == 0) continue;
    if (PrintVtables) {
         _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
                     "call sites in %s", count, hi->fname());
    }
    for (i = 0; i < count; i++) {
        // 內部將經常使用的 alloc、objc_msgSend 等函數指針進行註冊,並 fix 爲新的函數指針
        fixupMessageRef(refs+i);
    }
}
複製代碼
(6) readProtocol
// 遍歷全部協議列表,而且將協議列表加載到Protocol的哈希表中
for (EACH_HEADER) {
    extern objc_class OBJC_CLASS_$_Protocol;
    // cls = Protocol類,全部協議和對象的結構體都相似,isa都對應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);
    }
}
複製代碼
(7) remapProtocolRef
// 修復協議列表引用,優化後的 images 多是正確的,可是並不肯定
for (EACH_HEADER) {
    // 須要注意到是,下面的函數是 _getObjc2ProtocolRefs,和上面的 _getObjc2ProtocolList 不同
    protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
    for (i = 0; i < count; i++) {
        remapProtocolRef(&protolist[i]);
    }
}
複製代碼
(8) realizeClassWithoutSwift 分析

初始化類就在這一步,首先將非懶加載類從 Mach-O 裏面讀取出來,而後經過 realizeClassWithoutSwift 實例化 rw佈局

// 實現非懶加載的類,對於load方法和靜態實例變量
for (EACH_HEADER) {
    classref_t *classlist = 
        _getObjc2NonlazyClassList(hi, &count);
    for (i = 0; i < count; i++) {
        Class cls = remapClass(classlist[i]);
        if (!cls) continue;
        
        //... 忽略一些對 cls 的 cache 的一些操做
        
        addClassTableEntry(cls);

        //... 忽略可有可無的代碼
        
        // 實現全部非懶加載的類(實例化類對象的一些信息,例如rw)
        realizeClassWithoutSwift(cls);
    }
}

// 遍歷 resolvedFutureClasses 數組,實現懶加載的類
// resolvedFutureClasses 數組是在第二步的時候添加懶加載類的
if (resolvedFutureClasses) {
    for (i = 0; i < resolvedFutureClassCount; i++) {
        Class cls = resolvedFutureClasses[i];
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class is not allowed to be future");
        }
        // 實現懶加載的類
        realizeClassWithoutSwift(cls);
        cls->setInstancesRequireRawIsa(false/*inherited*/);
    }
    free(resolvedFutureClasses);
}  

static Class realizeClassWithoutSwift(Class cls)
{
    runtimeLock.assertLocked();

    const class_ro_t *ro;
    class_rw_t *rw;
    Class supercls;
    Class metacls;
    bool isMeta;

    if (!cls) return nil;
    // 判斷 cls 是否已經初始化,裏面是對 data()->flags 的判斷
    if (cls->isRealized()) return cls;
    assert(cls == remapClass(cls));

    // 驗證類不在共享緩存的未刪除部分
    ro = (const class_ro_t *)cls->data();
    // 判斷類是不是未實現的將來類
    if (ro->flags & RO_FUTURE) {
        // 是將來的類. rw 已經被初始化
        rw = cls->data();
        ro = cls->data()->ro;
        // 修改 flags
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // 正常的類. 分配可寫的類數據。
        // 開闢 rw 內存空間
        rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
        rw->ro = ro;
        rw->flags = RW_REALIZED|RW_REALIZING;
        cls->setData(rw);
    }
    
    // 判斷是不是元類
    isMeta = ro->flags & RO_META;

    rw->version = isMeta ? 7 : 0;  // old runtime went up to 6


    // 爲這個類選擇一個索引
    // 設置cls->instancesRequireRawIsa若是沒有更多的索引可用
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // 遞歸調用,實現父類和元類
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));

#if SUPPORT_NONPOINTER_ISA
    // 禁用一些類和非指針isa
    bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
    bool rawIsaIsInherited = false;
    static bool hackedDispatch = false;
     
    // 禁用非指針的 isa
    if (DisableNonpointerIsa) {
        // 非指針isa禁用的環境或應用程序SDK版本
        instancesRequireRawIsa = true;
    }
    else if (!hackedDispatch  &&  !(ro->flags & RO_META)  &&  
             0 == strcmp(ro->name, "OS_object")) 
    {
        // 在 hackedDispatch 裏 isa 也充當虛表指針
        hackedDispatch = true;
        instancesRequireRawIsa = true;
    }
    else if (supercls  &&  supercls->superclass  &&  
             supercls->instancesRequireRawIsa()) 
    {
        // 從元類到根元類設置
        instancesRequireRawIsa = true;
        rawIsaIsInherited = true;
    }
    
    if (instancesRequireRawIsa) {
        cls->setInstancesRequireRawIsa(rawIsaIsInherited);
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // 在從新映射時更新父類和元類
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

    // 協調實例變量的偏移量/佈局,可能會從新分配 class_ro_t,更新咱們的 ro 變量。
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // 若是尚未設置就開始設置 fastInstanceSize。
    cls->setInstanceSize(ro->instanceSize);

    // 將一些標誌從 ro 複製到 rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    
    // 從ro或父類中傳播關聯的對象禁止標誌
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // 將這個類鏈接到它的父類的子類列表,即雙向綁定
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // 修復 cls 的方法列表、協議列表和屬性列表,以及附加任何未完成的類別
    methodizeClass(cls);
    return cls;
}
複製代碼

methodizeClass 是將 ro 裏面的方法、協議以及屬性附加到 rw 裏面和把分類中的方法、協議和屬性添加到本類中,這也是分類是添加到本類中的時機,下面單獨講解一下這個方法。

static void methodizeClass(Class cls)
{
    runtimeLock.assertLocked();
    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro;

    // 將 ro 裏面的方法附加到 rw 裏面去
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        rw->methods.attachLists(&list, 1);
    }
    // 將 ro 裏面的屬性附加到 rw 裏面去
    property_list_t *proplist = ro->baseProperties;
    if (proplist) {
        rw->properties.attachLists(&proplist, 1);
    }
    // 將 ro 裏面的協議附加到 rw 裏面去
    protocol_list_t *protolist = ro->baseProtocols;
    if (protolist) {
        rw->protocols.attachLists(&protolist, 1);
    }

    // 根類得到額外的方法實現,若是它們尚未。這些適用於類別替換以前。
    if (cls->isRootMetaclass()) {
        // SEL SEL_initialize = NULL;
        addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories.
    // 返回類的未附加類別列表,並從列表中刪除它們。
    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    // 將類別中的方法列表、屬性和協議附加到類中
    attachCategories(cls, cats, false /*don’t flush caches*/);

    if (cats) free(cats);
    // ... 忽略一些可有可無的代碼
}
複製代碼

其中 attachLists 出現頻率很高,基本上方法、協議、屬性都是經過 attachLists 函數附加到對應的列表上的,接下來單獨介紹一下這個方法。

void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            // // 將 addedLists 移動到 array,memmove會對拷貝的數據做檢查,確保內存沒有覆蓋,若是發現會覆蓋數據,簡單的實現是調轉開始拷貝的位置,從尾部開始拷貝
            memmove(array()->lists + addedCount, array()->lists,
                    oldCount * sizeof(array()->lists[0]));
            // 將 addedLists 拷貝到 array()->lists,若是複製的兩個區域存在重疊時使用memcpy,其結果是不可預知的,有可能成功也有可能失敗的
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists,直接追到數組後面
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
}
複製代碼
(9) addUnattachedCategoryForClass
// 發現和處理全部Category
for (EACH_HEADER) {
    // 外部循環遍歷找到當前類,查找類對應的Category數組
    category_t **catlist = 
        _getObjc2CategoryList(hi, &count);
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();

    for (i = 0; i < count; i++) {
        // 內部循環遍歷當前類的全部Category
        category_t *cat = catlist[i];
        Class cls = remapClass(cat->cls);

        if (!cls) {
            // 類別的目標類丟失(多是弱連接)
            catlist[i] = nil;
            if (PrintConnecting) {
                _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                             "missing weak-linked target class", 
                             cat->name, cat);
            }
            continue;
        }

        // 首先,經過其所屬的類註冊Category。若是這個類已經被實現,則從新構造類的方法列表。
        bool classExists = NO;
        if (cat->instanceMethods ||  cat->protocols  
            ||  cat->instanceProperties) 
        {
            // 將Category添加到對應Class的value中,value是Class對應的全部category數組
            addUnattachedCategoryForClass(cat, cls, hi);
            // 將Category的method、protocol、property添加到Class
            // 判斷 cls 是否實現
            if (cls->isRealized()) {
                remethodizeClass(cls);
                classExists = YES;
            }
            if (PrintConnecting) {
                _objc_inform("CLASS: found category -%s(%s) %s", 
                             cls->nameForLogging(), cat->name, 
                             classExists ? "on existing class" : "");
            }
        }

        // 這塊和上面邏輯同樣,區別在於這塊是對Meta Class作操做,而上面則是對Class作操做
        // 根據下面的邏輯,從代碼的角度來講,是能夠對元類添加Category的
        if (cat->classMethods  ||  cat->protocols  
            ||  (hasClassProperties && cat->_classProperties)) 
        {
            addUnattachedCategoryForClass(cat, cls->ISA(), hi);
            if (cls->ISA()->isRealized()) {
                remethodizeClass(cls->ISA());
            }
            if (PrintConnecting) {
                _objc_inform("CLASS: found category +%s(%s)", 
                             cls->nameForLogging(), cat->name);
            }
        }
    }
    
    // 初始化從磁盤中加載的全部類,發現Category必須是最後執行的
    // 從runtime DebugNonFragileIvars字段一直是 false,因此不會進入這個方法中
    if (DebugNonFragileIvars) {
        realizeAllClasses();
    }
    
    //... 忽略一些打印的代碼
}
複製代碼
(10) 小拓展

read_images 裏常常出現 _getObjc2ClassRefs 之類的代碼,這是從 Mach-O 文件裏面讀取相應 setion 段的數據,咱們經過 MachOView 能夠看到相關 setion 段的信息。

3. load_images 分析

load_images 函數是對 load 方法的加載和調用,接下來咱們就來看看底層是怎麼對 load 處理的。

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    // 若是這裏沒有+load方法,則返回時不帶鎖。
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    {
        mutex_locker_t lock2(runtimeLock);
        // 準備 load 方法
        prepare_load_methods((const headerType *)mh);
    }

    // 調用 load 方法
    call_load_methods();
}
複製代碼

進到 prepare_load_methods 能夠看到系統是如何加載 load 方法的。

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++) {
        // 循環遍歷去加載非懶加載類的 load 方法到 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;  // category for ignored weak-linked class
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        // 若是本類沒有初始化就去初始化
        realizeClassWithoutSwift(cls);
        assert(cls->ISA()->isRealized());
        
        // 循環遍歷去加載非懶加載分類的 load 方法到 loadable_categories
        // 和非懶加載類差很少,就是數組不同
        add_category_to_loadable_list(cat);
    }
}

static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

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

    // 常規操做,遞歸調用父類加載 load 方法
    schedule_class_load(cls->superclass);
    // 將 load 方法加載到 loadable_classes
    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}

void add_class_to_loadable_list(Class cls)
{
    IMP method;
    loadMethodLock.assertLocked();
    // 獲取 load 方法的 imp
    method = cls->getLoadMethod();
    // 若是沒有 load 方法直接返回
    if (!method) return; 
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    // 擴容
    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 添加 load 方法
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}
複製代碼

從上面準備 load 方法的代碼,能夠看出,是先去加載父類的,而後再加載本類,以後再去加載分類的 load 方法。咱們再看看是怎麼調用 load 方法的。

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

    // 保證只調用一次
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();
    // do while 循環調用 load 方法
    do {
        // 1.重複調用非懶加載類的 load,直到沒有更多的
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2.調用非懶加載分類的 load 方法,和非懶加載類差很少
        more_categories = call_category_loads();

        // 3. 若是有類或更多何嘗試的類別,則運行更多 load
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);
    loading = NO;
}

static void call_class_loads(void)
{
    int i;
    // 取出 loadable_classes
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // 調用保存在 loadable_classes 裏的 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; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        // 發送 load 消息
        (*load_method)(cls, SEL_load);
    }
    
    // 釋放內存
    if (classes) free(classes);
}
複製代碼
  • 總結看 load 的調用,能夠得出,load 的調用順序是:父類->本類->分類

4. unmap_image 分析

unmap_image 是用來處理將被 dyld 取消映射的給定 images

void 
unmap_image(const char *path __unused, const struct mach_header *mh)
{
    recursive_mutex_locker_t lock(loadMethodLock);
    mutex_locker_t lock2(runtimeLock);
    unmap_image_nolock(mh);
}

void 
unmap_image_nolock(const struct mach_header *mh)
{
    if (PrintImages) {
        _objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
    }

    header_info *hi;
    
    // 查找映像的運行時 header_info 結構
    for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
        if (hi->mhdr() == (const headerType *)mh) {
            break;
        }
    }

    if (!hi) return;

    if (PrintImages) {
        _objc_inform("IMAGES: unloading image for %s%s%s\n", 
                     hi->fname(),
                     hi->mhdr()->filetype == MH_BUNDLE ? " (bundle)" : "",
                     hi->info()->isReplacement() ? " (replacement)" : "");
    }
    // 目前只處理 MH_BUNDLE
    _unload_image(hi);

    // 從標題列表中刪除 header_info
    removeHeader(hi);
    free(hi);
}
複製代碼

4、 總結

  • 1.類的加載會先來到 _objc_init 函數,執行 _dyld_objc_notify_register,再經過 dyldregisterObjCNotifiers 回調到 _dyld_objc_notify_register 並執行 map_imagesload_imagesunmap_image
  • 2.map_images 加載鏡像文件,_read_images 讀取鏡像文件並加載類。
  • 3.第一次調用 _read_images 的時候會去初始化兩張表 gdb_objc_realized_classesallocatedClasses 進行類信息的存儲。
  • 4.初始化表以後就是調用 readClass 是將類插入到 allocatedClasses 表中的。
  • 5.而後再經過 remapClassRef 進行修復類的重映射和 fixupMessageRef 修復舊的函數指針調用遺留。
  • 6.類處理完以後就是添加協議和方法都註冊到哈希表中,方便之後對其進行調用。
  • 7.再經過 realizeClassWithoutSwift 去初始化類,其中是對類的 rw 實例化,以及將 ro 數據賦值到 rw 上。
  • 8.load_images 是對 load 方法的處理以及調用,調用順序是 父類->本類->分類,而有多個分類 load 的時候是根據編譯順序執行的。
  • 9.unmap_image 是用來處理將被 dyld 取消映射的給定 images。
相關文章
相關標籤/搜索