Objective-C 小記(5)類的加載

本文使用的 runtime 版本為 objc4-706。

Runtime 的入口,是可以在 objc-os.mm 找到的函數(shù) _objc_init,它被 libSystem 調(diào)用,進(jìn)行初始化和加載 image,所謂 image,在 macOS 和 iOS 上來說,就是 Mach-O 文件。

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_2_images, load_images, unmap_image);
}

在上面 _objc_init 的代碼中,除去初始化函數(shù)的調(diào)用,最后一行是對 _dyld_objc_notify_register 的調(diào)用,這個(gè)函數(shù)接收三個(gè)函數(shù)當(dāng)作回調(diào),傳遞 image 信息讓其自行處理。

其中 map_2_images 函數(shù)就是 image 中的 Objective-C 類的信息進(jìn)入 runtime 發(fā)生的地方。

void
map_2_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);
}

map_2_images 函數(shù)在加鎖后就轉(zhuǎn)向了 map_images_nolock 函數(shù)。

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);
    }
    
    ...
}

map_images_nolock 函數(shù)中,它檢查傳入的每個(gè) image,如果 image 有 runtime 需要的信息,就將它記錄在 hList 中,并將 hCount 加一,最終判斷 hCount 來決定繼續(xù)讀取 image 中的數(shù)據(jù),即 _read_images 函數(shù)。

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    ...
    
#define EACH_HEADER \
    hIndex = 0;         \
    hIndex < hCount && (hi = hList[hIndex]); \
    hIndex++

    ...
    
    // Discover classes. Fix up unresolved future classes. Mark bundle classes.

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

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

        classref_t *classlist = _getObjc2ClassList(hi, &count);
        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;
            }
        }
    }
    
    ...
    
    // Realize non-lazy classes (for +load methods and static instances)
    for (EACH_HEADER) {
        classref_t *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            realizeClass(cls);
        }
    }

    ...
    
#undef EACH_HEADER
}

_read_images 函數(shù)中有兩段關(guān)于類加載的部分。第一部分將每個(gè) image 里的類信息都經(jīng)過 readClass 函數(shù)處理一遍,將類的名字和類本身存到一個(gè)表里,這樣就可以使用 objc_getClass 函數(shù)通過名字來獲取類了。第二部分讓 image 里的類經(jīng) realizedClass 函數(shù)處理一遍,先回顧一下類的定義。

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};

struct class_rw_t {
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;
    
    ...
    
};

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 
        return bits.data();
    }
    
    ...

};

可以看到定義中,objc_classdata 方法返回類型是 class_rw_t *,但是在 image 中的類信息,data 返回的實(shí)際上是 class_ro_t *,這就需要 realizeClass 函數(shù)進(jìn)行一些適當(dāng)?shù)牟僮鲗⑦@些數(shù)據(jù)整對。

static Class realizeClass(Class cls)
{
    ...

    ro = (const class_ro_t *)cls->data();
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro;
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
        rw->ro = ro;
        rw->flags = RW_REALIZED|RW_REALIZING;
        cls->setData(rw);
    }
    
    ...

    supercls = realizeClass(remapClass(cls->superclass));
    metacls = realizeClass(remapClass(cls->ISA()));

    // Update superclass and metaclass in case of remapping
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // Set fastInstanceSize if it wasn't set already.
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }

    // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // Attach categories
    methodizeClass(cls);

    return cls;
}

realizeClass 函數(shù)會(huì)給類創(chuàng)建 class_rw_t * 的數(shù)據(jù),并將 ro 里的一些東西拷貝過來,順帶給父類和元類也 realizeClass 一下。最后使用 methodizeClass 函數(shù)將 ro 中方法、屬性這些信息也拷貝到類的 class_rw_t * 中來。

總結(jié)

總的來說,類需要先從 Mach-O 文件中 map 過來,再進(jìn)行 realize。但其中環(huán)繞太多細(xì)節(jié),這篇虎頭蛇尾的文章就算一個(gè)隨意的記錄了。:-(

參考

iOS 程序 main 函數(shù)之前發(fā)生了什么 · sunnyxx的技術(shù)博客
Mach-O Executables · objc.io

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

相關(guān)閱讀更多精彩內(nèi)容

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