iOS底層原理之類的加載探究

1 前言

咱們從iOS底層原理之—dyld與objc的關聯中知道dyld關聯objc時,經過_read_images函數中的readClass函數來讀取類的信息並將類關聯起來,咱們本文主要來探討類的加載。markdown

2 先看_read_images函數

因爲咱們探究的是類的加載,且在iOS底層原理之—dyld與objc的關聯中咱們已經主要分析了下_read_images函數的做用,所以咱們主要看_read_images函數中有關的部分代碼,上代碼app

// Discover classes. Fix up unresolved future classes. Mark bundle classes.
    bool hasDyldRoots = dyld_shared_cache_some_image_overridden();

    for (EACH_HEADER) {
        if (! mustReadClasses(hi, hasDyldRoots)) {
            // Image is sufficiently optimized that we need not call readClass()
            continue;
        }

        classref_t const *classlist = _getObjc2ClassList(hi, &count);

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

        for (i = 0; i < count; i++) {
            Class cls = (Class)classlist[i];
            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;
            }
        }
    }

    ts.log("IMAGE TIMES: discover classes");
複製代碼

2.1 分析調用readClass部分代碼

2.1.1 沒有調用readClass以前cls分析

在沒有調用readClass時,咱們讀取cls信息從上圖中咱們發現此時cls只是一串地址函數

2.1.2 調用readClass以後cls分析

調用readClass以後,咱們讀取cls信息從上圖中咱們得知cls已經由一串地址變成了LGPerson類。所以咱們得出必定是readClsss函數作了什麼,咱們來分析下readClsss函數。post

3 readClsss函數分析

3.1 源碼

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName  = cls->mangledName();
    const char *LGPersonName = "LGPerson";
    if (strcmp(mangledName, LGPersonName) == 0) {
        auto kc_ro = (const class_ro_t *)cls->data();
        printf("readClass: 這個是我要研究的 %s \n",LGPersonName);
    }
    if (missingWeakSuperclass(cls)) {
        // No superclass (probably weak-linked). 
        // Disavow any knowledge of this subclass.
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", 
                         cls->nameForLogging());
        }
        addRemappedClass(cls, nil);
        cls->superclass = nil;
        return nil;
    }
    cls->fixupBackwardDeployingStableSwift();
    Class replacing = nil;
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // This name was previously allocated as a future class.
        // Copy objc_class to future class's struct.
        // Preserve future's rw data block.
        if (newCls->isAnySwift()) {
            _objc_fatal("Can't complete future class request for '%s' "
                        "because the real class is too big.", 
                        cls->nameForLogging());
        }
        class_rw_t *rw = newCls->data();
        const class_ro_t *old_ro = rw->ro();
        memcpy(newCls, cls, sizeof(objc_class));
        rw->set_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) {
        // class list built in shared cache
        // fixme strict assert doesn't work because of duplicates
        // ASSERT(cls == getClass(name));
        ASSERT(getClassExceptSomeSwift(mangledName));
    } else {
        addNamedClass(cls, mangledName, replacing);
        addClassTableEntry(cls);
    }

    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    return cls;
}
複製代碼

源碼中爲了更好的區別出LGPerson,特地添加了判斷LGPerson類型的代碼ui

3.2 代碼跟蹤

經過代碼調試,最終執行了addNamedClsss函數在執行addNamedClsss函數以前打印cls依然是一串地址。當執行完addNamedClsss函數時,打印cls發現指向了LGPerson所以咱們得出addNamedClsss對cls進行了指向操做。this

3.3 addNamedClsss函數

4 懶加載類與非懶加載類

4.1 定義

  • 懶加載類:推遲到第一次消息發送的時候才加載的類稱爲懶加載類
  • 非懶加載類:在map_images函數執行_read_images函數時就加載的類,稱爲非懶加載類
  • 通俗理解就是當某個類實現了+load函數,則就是非懶加載類,沒有實現就是懶加載類

4.2 懶加載類分析

對於LGPerson類不實現+load函數,咱們從前面分析知道_read_images中有處理非懶加載類代碼,咱們打上斷點以及打印,如圖因爲LGPerson沒有實現+load函數,因此是懶加載類,所以沒有進入咱們的斷點。然而LGPerson類在何時加載了呢?咱們研究 非懶加載類的代碼發現,實現了realizeClassWithoutSwift函數,所以咱們看懶加載類會不會進入這個函數。spa

4.2.1 realizeClassWithoutSwift

從這裏咱們驗證了懶加載類的類的加載延遲到第一次消息發送ssr

4.3 非懶加載類分析

咱們讓LGPerson實現+load函數,裏面什麼也不用作,如圖此時看_read_images中有關非懶加載類的處理此時咱們看到進入了咱們原先的斷點中,且也執行了realizeClassWithoutSwift函數3d

4.4 重點分析realizeClassWithoutSwift函數

重點代碼分析 調試

類加載總結

相關文章
相關標籤/搜索