Runtime源代碼解讀2(類和對象)

2019-10-10git

Runtime源代碼解讀(實現面向對象初探)中,從Cocoa框架中的runtime.h頭文件公開的數據結構及 API 對 runtime 總體有一個大概的認知。從本文開始具體分析 Apple 開源的runtime源代碼。本文介紹 runtime 如何經過C語言結構體實現類和對象,該部分應該是 runtime 的最核心代碼。github

注意:Github 搜索到的有一千多顆星的RetVal/objc-runtime工程,版本是750,最新公開的代碼版本是756,後者在 ARC 支持、ivarLayout定義、Swift 兼容等方面有變更。swift

1、類的定義

Objective-C 中類的本質是objc_class結構體,其定義代碼以下,包含如下成員:數組

  • isaobjc_class繼承objc_object結構體,所以也包含isa指針,主要功能是指向對象的類型,新版本 runtime 中,isa指針並不必定是Class類型而是包含64 bit 數據的位圖(bitmap),在 4.1 中詳細介紹;
  • superclass:指向父類的指針,用於組織類的繼承鏈;
  • cache:類使用哈希表數據結構緩存最近調用方法,以提升方法查找效率(TODO:後續獨立文章中會介紹);
  • bitsclass_data_bits_t結構體類型,該結構體主要用於記錄,保存類的數據的class_rw_t結構體的內存地址。經過date()方法訪問bits的有效位域指向的內存空間,返回class_rw_t結構體;setData(class_rw_t *newData)用於設置bits的值;

注意:上述 bitmap 並非圖片的位圖,而是指數據被視爲簡單的二進制數,將其中的一些或全部 bit 賦予特殊的含義,共同表示一種含義的 bit 或 bit 的集合稱爲位域。緩存

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();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    // 類的方法在文章第三部分詳細介紹
    ...

};
複製代碼

2、類的數據

類的數據主要保存在class_data_bits_t結構體中,其成員僅有一個bits指針。objc_classdata()方法用於獲取bits成員的 4~47 位域(FAST_DATA_MASK)中保存的class_rw_t結構體地址。類的數據保存在class_rw_t結構體中,剩餘的部分保存在ro指針指向的class_ro_t結構體中。bash

class_rw_tclass_ro_t結構體名中,rw是 read write 的縮寫,ro是 read only 的縮寫,可見class_ro_t的保存類的只讀信息,這些信息在類完成註冊後不可改變。以類的成員變量列表爲例(成員變量列表保存在class_ro_t結構體中)。若應用類註冊到內存後,使用類構建了若干實例,此時若添加成員變量必然須要對內存中的這些類從新分配內存,這個操做的花銷是至關大的。若考慮再極端一些,爲根類NSObject添加成員變量,則內存中基本全部 Objective-C 對象都須要從新分配內存,如此龐大的計算量在運行時是不可接受的。數據結構

#if !__LP64__
#define FAST_DATA_MASK 0xfffffffcUL
#elif 1
#define FAST_DATA_MASK 0x00007ffffffffff8UL
#endif

#if (!__LP64__ || TARGET_OS_WIN32 || \
     (TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC))
# define SUPPORT_PACKED_ISA 0
#else
# define SUPPORT_PACKED_ISA 1
#endif

#if !SUPPORT_INDEXED_ISA && !SUPPORT_PACKED_ISA
# define SUPPORT_NONPOINTER_ISA 0
#else
# define SUPPORT_NONPOINTER_ISA 1
#endif

struct class_data_bits_t {
    uintptr_t bits;

private:
    bool getBit(uintptr_t bit)
    {
        return bits & bit;
    }

    ...

public:

    // 獲取類的數據
    class_rw_t* data() {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }

    // 設置類的數據
    void setData(class_rw_t *newData)
    {
        // 僅在類註冊、構建階段才容許調用setData
        assert(!data()  ||  (newData->flags & (RW_REALIZING | RW_FUTURE)));
        uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
        atomic_thread_fence(memory_order_release);
        bits = newBits;
    }

    ...

// 是否支持非指針類型isa,在4.1介紹對象的isa指針時詳細介紹
#if FAST_REQUIRES_RAW_ISA
    bool instancesRequireRawIsa() {
        return getBit(FAST_REQUIRES_RAW_ISA);
    }
    void setInstancesRequireRawIsa() {
        setBits(FAST_REQUIRES_RAW_ISA);
    }
#elif SUPPORT_NONPOINTER_ISA
    // 主流機型通常走到這個編譯分支
    bool instancesRequireRawIsa() {
        return data()->flags & RW_REQUIRES_RAW_ISA;
    }
    void setInstancesRequireRawIsa() {
        data()->setFlags(RW_REQUIRES_RAW_ISA);
    }
#else
    bool instancesRequireRawIsa() {
        return true;
    }
    void setInstancesRequireRawIsa() {
        // nothing
    }
#endif

    ...

};
複製代碼

1.1 class_rw_t 結構體

類的主要數據保存在bits中,bits以位圖保存class_rw_t結構體,用於記錄類的關鍵數據,如成員變量列表、方法列表、屬性列表、協議列表等等,class_rw_t僅包含三個基本的位操做方法。class_rw_t包含如下成員:架構

  • flags:32位位圖,標記類的狀態;
  • version:標記類的類型,0表示類爲非元類,7表示類爲元類;
  • ro:保存類的只讀數據,註冊類後ro中的數據標記爲只讀,成員變量列表保存在ro中;
  • methods:方法列表,其類型method_array_t爲二維數組容器(TODO:後續在獨立文章介紹);
  • properties:屬性列表,其類型property_array_t爲二維數組容器(TODO:後續在獨立文章介紹);
  • protocols:協議列表,其類型protocol_array_t爲二維數組容器;
  • firstSubclass:類的首個子類,與nextSiblingClass記錄全部類的繼承鏈組織成的繼承樹;
  • nextSiblingClass:類的下一個兄弟類;
  • demangledName:類名,來自Swift的類會包含一些特別前綴,demangledName是處理後的類名;
  • index:標記類的對象的isa是否爲index類型;
#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
# define SUPPORT_INDEXED_ISA 1
#else
# define SUPPORT_INDEXED_ISA 0
#endif

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;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif

    //設置set指定的位
    void setFlags(uint32_t set) 
    {
        OSAtomicOr32Barrier(set, &flags);
    }

    // 清空clear指定的位
    void clearFlags(uint32_t clear) 
    {
        OSAtomicXor32Barrier(clear, &flags);
    }

    // 設置set指定的位,清空clear指定的位
    void changeFlags(uint32_t set, uint32_t clear) 
    {
        assert((set & clear) == 0);

        uint32_t oldf, newf;
        do {
            oldf = flags;
            newf = (oldf | set) & ~clear;
        } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
    }

};
複製代碼

class_rw_tflags成員中比較重要的一些位域定義列舉以下,均以RW_爲前綴,這些位域在類註冊後仍可讀寫。app

/************* 類註冊後可讀寫的flags位域 *************/
// 類是已經註冊的類
#define RW_REALIZED (1<<31)
// 類是還沒有解析的future class
#define RW_FUTURE (1<<30)
// 類是已經初始化的類
#define RW_INITIALIZED (1<<29)
// 類是正在初始化的類
#define RW_INITIALIZING (1<<28)
// class_rw_t->ro是class_ro_t的堆拷貝
// 此時類的class_rw_t->ro是可寫入的,拷貝以前ro的內存區域鎖死不可寫入
#define RW_COPIED_RO (1<<27)
// 類是正在構建而仍未註冊的類
#define RW_CONSTRUCTING (1<<26)
// 類是已經構建完成並註冊的類
#define RW_CONSTRUCTED (1<<25)
// 類是load方法已經調用過的類
#define RW_LOADED (1<<23)
#if !SUPPORT_NONPOINTER_ISA
// 類是可能實例可能存在關聯對象的類
// 默認編譯選項下,無需定義該位,由於均可能有關聯對象
#define RW_INSTANCES_HAVE_ASSOCIATED_OBJECTS (1<<22)
#endif
// 類是具備實例相關的GC layout的類
#define RW_HAS_INSTANCE_SPECIFIC_LAYOUT (1 << 21)
// 類是禁止使用關聯對象的類
#define RW_FORBIDS_ASSOCIATED_OBJECTS (1<<20)
// 類是正在註冊,可是未註冊完成的類
#define RW_REALIZING (1<<19)
複製代碼

1.1 class_ro_t 結構體

類完成註冊後,類的實例佔用的內存大小、成員變量列表、成員變量內存佈局等重要信息須要固定下來,這些在類註冊後須要標記爲只讀的數據保存在class_ro_t結構體中,class_rw_t結構體的ro成員爲指向該結構體的指針。class_ro_t結構體包含如下主要成員:框架

  • flags:32位位圖,標記類的狀態。須要注意class_ro_tflags使用的位域和前面介紹的class_rw_tflags使用的位域是徹底不一樣的;
  • instanceStart:類的成員變量,在實例的內存空間中的起始偏移量;
  • instanceSize:類的實例佔用的內存空間大小;
  • ivarLayout:成員變量內存佈局,標記實例佔用的內存空間中哪些WORD保存了成員變量數據;
  • name:類名;
  • baseMethodList:基礎方法列表,在類定義時指定的方法列表;
  • baseProtocols:協議列表;
  • ivars:成員變量列表;
  • weakIvarLayout:weak成員變量佈局;
  • baseProperties:基礎屬性列表,在類定義時指定的屬性列表;
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;
    }

    class_ro_t *duplicate() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
            return ro;
        } else {
            size_t size = sizeof(*this);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            return ro;
        }
    }
};
複製代碼

class_ro_tflags成員中比較重要的一些位域定義列舉以下,均以RO_爲前綴,這些位域在類註冊後標記爲只讀。

/************* 類註冊後只讀的flags位域 *************/
// 類是元類
#define RO_META (1<<0)
// 類是根類
#define RO_ROOT (1<<1)
// 類有CXX構造/析構函數
#define RO_HAS_CXX_STRUCTORS (1<<2)
// 類有實現load方法
// #define RO_HAS_LOAD_METHOD (1<<3)
// 隱藏類
#define RO_HIDDEN (1<<4)
// class has attribute(objc_exception): OBJC_EHTYPE_$_ThisClass is non-weak
#define RO_EXCEPTION (1<<5)
// class has ro field for Swift metadata initializer callback
#define RO_HAS_SWIFT_INITIALIZER (1<<6)
// 類使用ARC選項編譯
#define RO_IS_ARC (1<<7)
// 類有CXX析構函數,但沒有CXX構造函數
#define RO_HAS_CXX_DTOR_ONLY (1<<8)
// class is not ARC but has ARC-style weak ivar layout 
#define RO_HAS_WEAK_WITHOUT_ARC (1<<9)
// 類禁止使用關聯對象
#define RO_FORBIDS_ASSOCIATED_OBJECTS (1<<10)

// class is in an unloadable bundle - must never be set by compiler
#define RO_FROM_BUNDLE (1<<29)
// class is unrealized future class - must never be set by compiler
#define RO_FUTURE (1<<30)
// class is realized - must never be set by compiler
#define RO_REALIZED (1<<31)
複製代碼

注意:實際上在類的構建階段,有時會操做class_rw_t去置flags中一些RO_前綴的位域,但僅發生在重疊的29/30/31位域。

3、類的行爲

本章介紹objc_class結構體中定義的方法。

3.1 類加載過程相關行爲

調用 runtime API 動態建立類的過程,包括三個步驟:

  • 調用Class objc_allocateClassPair(...)構建類;
  • 添加必要的成員變量、方法等元素;
  • 調用void objc_registerClassPair(Class cls)註冊類;

然而,runtime 從鏡像(image)加載類的過程會更加精細,在加載類的不一樣階段會被標記爲不一樣的類型(仍是objc_class結構體,只是flags不一樣),例如:future class(懶加載類)、remapped class(已重映射類)、realized class(已認識類)、allocated class(已分配內存類)、named class(已肯定名稱類)、loaded class(已加載類)、initialized class(已初始化類)等。接下來重點介紹 future class、remapped class 和 realized class。其中標記爲 allocated class 和 named class 只是簡單地將類添加到全局管理的哈希表中,所以僅穿插在 future class、remapped class 中介紹;loaded class、initialized class 分別爲已執行load方法的類和已執行initialize()方法的類。

objc_class結構體中與類的加載過程相關的函數代碼以下,基本在class_rw_tclass_ro_tflags中存在RW_RO_前綴的位域與之對應:

// 查詢是否正在初始化(initializing)
    bool isInitializing() {
        return getMeta()->data()->flags & RW_INITIALIZING;
    }

    // 標記爲正在初始化(initializing)
    void setInitializing() {
        assert(!isMetaClass());
        ISA()->setInfo(RW_INITIALIZING);
    }

    // 是否已完成初始化(initializing)
    bool isInitialized() {
        return getMeta()->data()->flags & RW_INITIALIZED;
    }

    void setInitialized(){
        Class metacls;
        Class cls;

        assert(!isMetaClass());

        cls = (Class)this;
        metacls = cls->ISA();

        // 關於alloc/dealloc/Retain/Release等特殊方法的判斷及處理
        ...

        metacls->changeInfo(RW_INITIALIZED, RW_INITIALIZING);
    }

    bool isLoadable() {
        assert(isRealized());
        return true;  // any class registered for +load is definitely loadable
    }

    // 獲取load方法的IMP
    IMP 
    objc_class::getLoadMethod()
    {
        runtimeLock.assertLocked();

        const method_list_t *mlist;

        assert(isRealized());
        assert(ISA()->isRealized());
        assert(!isMetaClass());
        assert(ISA()->isMetaClass());

        // 在類的基礎方法列表中查詢load方法的IMP
        mlist = ISA()->data()->ro->baseMethods();
        if (mlist) {
            for (const auto& meth : *mlist) {
                const char *name = sel_cname(meth.name);
                if (0 == strcmp(name, "load")) {
                    return meth.imp;
                }
            }
        }

        return nil;
    }

    // runtime是否已認識類
    bool isRealized() {
        return data()->flags & RW_REALIZED;
    }

    // 是否future class
    bool isFuture() { 
        return data()->flags & RW_FUTURE;
    }
複製代碼

3.1.1 future class

objc_classisFuture()函數,用於判斷類是否爲 future class。本節經過代碼一步步探討 future class 的概念,future class 對理解類的加載過程有重要做用。

3.1.1.1 future class 生成

首先看 future class 是如何生成的addFutureNamedClass(const char *name, Class cls)函數用於將傳入的cls參數,配置爲類名爲name的 future class,包含如下操做:

  • 分配cls所需的class_rw_tclass_ro_t的內存空間;
  • cls的類名置爲name
  • class_rw_tRO_FUTURE位置爲1,RO_FUTURE等於RW_FUTURE
  • name爲關鍵字,將cls添加到一個全局的哈希表futureNamedClasses
static NXMapTable *future_named_class_map = nil;
static NXMapTable *futureNamedClasses()
{
    runtimeLock.assertLocked();
    
    if (future_named_class_map) return future_named_class_map;

    // future_named_class_map is big enough for CF’s classes and a few others
    future_named_class_map = 
        NXCreateMapTable(NXStrValueMapPrototype, 32);

    return future_named_class_map;
}

static void addFutureNamedClass(const char *name, Class cls)
{
    void *old;

    class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
    class_ro_t *ro = (class_ro_t *)calloc(sizeof(class_ro_t), 1);
    ro->name = strdupIfMutable(name);
    rw->ro = ro;
    cls->setData(rw);
    cls->data()->flags = RO_FUTURE; 

    old = NXMapKeyCopyingInsert(futureNamedClasses(), name, cls);
    assert(!old);
}
複製代碼

追蹤調用addFutureClass(...)的代碼,最終追溯到Class objc_getFutureClass(const char *name),該函數並無在 runtime 源代碼中被調用到。而用於從namedFutureClasses哈希表中獲取 future class 的popFutureClass(...)函數是有間接經過readClass(...)函數被普遍調用。所以,構建 future class 的邏輯大多隱藏在 runtime 的內部實現中未公佈,只有使用 future class 的邏輯是開源的

Class objc_getFutureClass(const char *name)
{
    Class cls;

    cls = look_up_class(name, YES, NO);
    if (cls) {
        if (PrintFuture) {
            _objc_inform("FUTURE: found %p already in use for %s", 
                         (void*)cls, name);
        }

        return cls;
    }
    
    // 若查找不到名爲name的類則構建future class
    return _objc_allocateFutureClass(name);  
}

Class _objc_allocateFutureClass(const char *name)
{
    mutex_locker_t lock(runtimeLock);

    Class cls;
    NXMapTable *map = futureNamedClasses();

    if ((cls = (Class)NXMapGet(map, name))) {
        // 存在名爲name的 future class
        return cls;
    }

    // 分配用於保存objc_class的內存空間
    cls = _calloc_class(sizeof(objc_class));  

    // 構建名爲name的future class並全局記錄到 futureNamedClasses 哈希表
    addFutureNamedClass(name, cls);  

    return cls;
}
複製代碼
3.1.1.2 future class 應用

addFutureClass(...)操做明顯是全局記錄 future class 的過程,接下來追溯 什麼時候用到 future classstatic Class popFutureNamedClass(const char *name)用於從futureNamedClasses哈希表中彈出類名爲name的 future class,這是獲取全局記錄的 future class 的惟一入口。

static Class popFutureNamedClass(const char *name)
{
    runtimeLock.assertLocked();

    Class cls = nil;

    if (future_named_class_map) {
        cls = (Class)NXMapKeyFreeingRemove(future_named_class_map, name);
        if (cls && NXCountMapTable(future_named_class_map) == 0) {
            NXFreeMapTable(future_named_class_map);
            future_named_class_map = nil;
        }
    }

    return cls;
}
複製代碼

popFutureNamedClassClass readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)函數中有被調用到,後者用於讀取cls中的類數據,關鍵處理邏輯表述以下:

  • futureNamedClasses哈希表中存在cls->mangledName()類名的 future class,則將cls重映射(remapping)到新的類newCls(具體重映射過程在 3.1.2 中詳細討論),而後將newCls標記爲 remapped class,以cls爲關鍵字添加到全局記錄的remappedClasses()哈希表中;
  • cls標記爲 named class,以cls->mangledName()類名爲關鍵字添加到全局記錄的gdb_objc_realized_classes哈希表中,表示 runtime 開始能夠經過類名查找類(注意元類不須要添加);
  • cls及其元類標記爲 allocated class,並將二者均添加到全局記錄的allocatedClasses哈希表中(無需關鍵字),表示已爲類分配固定內存空間;

注意:傳入readClass(...)cls參數是Class類型,而函數返回結果也是Class,爲何讀取類信息是「從類中讀取類信息」這樣怪異的過程呢?實際上是由於cls參數來源於 runtime 未開源的 從鏡像(image)中讀取類的過程,該過程輸出的objc_class存在特殊之處:要麼輸出 future class,要麼輸出普通類可是其bits指向的是class_ro_t結構體而非class_rw_t,之因此如此是由於從鏡像讀取的是編譯時決議的靜態數據,原本就應該保存在class_ro_t結構體中。

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->mangledName();
    
    //類的繼承鏈上,存在既不是根類(RO_ROOT位爲0)又沒有超類的類,則爲missingWeakSuperclass
    //注意:這是惟一的向remappedClasses中添加nil值的入口
    if (missingWeakSuperclass(cls)) {
        addRemappedClass(cls, nil);
        cls->superclass = nil;
        return nil;
    }
    
    // 兼容舊版本libobjc的配置,可忽略
    cls->fixupBackwardDeployingStableSwift();

    Class replacing = nil;
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // 已經全局記錄該類名的 future class
        // 構建newCls並將cls的內容拷貝到其中,保存future class的rw中的數據
        // 以cls爲關鍵字將構建的newCls添加到全局記錄的remappedClasses哈希表中

        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) {
        // 已存在該類名的named class
        assert(getClassExceptSomeSwift(mangledName));
    } else {
        // 將類添加到 named classes
        addNamedClass(cls, mangledName, replacing);

        // 將類添加到 allocated classes
        addClassTableEntry(cls);
    }

    // 設置RO_FROM_BUNDLE位
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    
    return cls;
}
複製代碼

從上文readClass(...)代碼if (Class newCls = popFutureNamedClass(mangledName))分支內free((void *)old_ro)語句,得出在cls映射到newCls過程當中,徹底丟棄了 future class 的ro數據。最後,結合以上全部代碼,能夠概括如下結論:

  • Future class 類的有效數據實際上僅有:類名和rwrw中的數據做用也很是少,僅使用flagsRO_FUTURE(實際上就是RW_FUTURE)標記類是 future class;
  • Future class 的做用是爲指定類名的類,提早分配好內存空間,調用readClass(...)函數讀取類時,才正式寫入類的數據。 Future class 是用於支持類的懶加載機制;

3.1.2 remapped class

在上文 3.1.1 有提到類的重映射,重映射的類被標記爲 remapped class,並以映射前的類爲關鍵字,添加到全局的remappedClass哈希表中。回顧Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)函數中,類的重映射代碼以下,關於處理過程的詳細描述已註釋到代碼中:

// 1. 若該類名已被標記爲future class,則彈出該類名對應的future class 賦值給newCls
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // 2. rw記錄future class的rw
        class_rw_t *rw = newCls->data();
        // 3. future class的ro記爲old_ro,後面釋放其佔用的內存空間並丟棄
        const class_ro_t *old_ro = rw->ro;
        // 4. 將cls中的數據拷貝到newCls,主要是要沿用cls的isa、superclass和cache數據
        memcpy(newCls, cls, sizeof(objc_class));
        // 5. rw記錄cls的ro
        rw->ro = (class_ro_t *)newCls->data();
        // 6. 沿用future class的rw、cls的ro
        newCls->setData(rw);
        // 7. 釋放future class的ro佔用的空間
        freeIfMutable((char *)old_ro->name);
        free((void *)old_ro);
        
        // 8. 將newCls以cls爲關鍵字添加到remappedClasses哈希表中
        addRemappedClass(cls, newCls);
        
        replacing = cls;
        cls = newCls;
    }
複製代碼

綜合上面代碼的詳細註釋,可知cls重映射到newCls後,newCls的數據保留了cls中的superclasscache成員,可是bits中指向class_rw_t結構體地址的位域(FAST_DATA_MASK)指向了新的class_rw_t結構體。該結構體的ro指針指向cls->data()所指向的內存空間中保存的class_ro_t結構體,其餘數據則是直接沿用 從namedFutureClasses哈希表中彈出的 future class 的class_rw_t結構體(經過future class 的data()方法返回)中數據。

注意:雖然objc_classdata()方法聲明爲返回class_rw_t *,可是究其本質,它只是返回了objc_classbits成員的FAST_DATA_MASK標記的位域中保存的內存地址,該內存地址實際上能夠保存任何類型的數據。在Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)函數中,傳入的cls所指向的objc_class結構體有其特殊之處:clsbits成員的FAST_DATA_MASK位域,指向的內存空間保存的是class_ro_t結構體,並非一般的class_rw_t

上述只是對 future class 的重映射過程,通用的類重映射調用static class remapClass(Class cls),注意當傳入的cls類不在remappedClasses哈希表中時,直接返回cls自己;static void remapClassRef(Class *clsref)可對傳入的Class* clsref重映射(改變*clsref的值),返回時clsref將 指向*clsref重映射後的類。類的重映射相關代碼以下:

// 獲取remappedClasses,保存已重映射的全部類的全局哈希表
static NXMapTable *remappedClasses(bool create)
{
    // 靜態的全局哈希表,沒有找到remove接口,只會無限擴張
    static NXMapTable *remapped_class_map = nil;

    runtimeLock.assertLocked();

    if (remapped_class_map) return remapped_class_map;
    if (!create) return nil;

    // remapped_class_map is big enough to hold CF’s classes and a few others
    INIT_ONCE_PTR(remapped_class_map, 
                  NXCreateMapTable(NXPtrValueMapPrototype, 32), 
                  NXFreeMapTable(v));

    return remapped_class_map;
}

// 將oldcls重映射獲得的newcls,以oldcls爲關鍵字插入到remappedClasses哈希表中
// 注意:從代碼透露出來的信息是,remappedClasses中只保存 future class 重映射的類
static void addRemappedClass(Class oldcls, Class newcls)
{
    runtimeLock.assertLocked();

    if (PrintFuture) {
        _objc_inform("FUTURE: using %p instead of %p for %s", 
                     (void*)newcls, (void*)oldcls, oldcls->nameForLogging());
    }

    void *old;
    old = NXMapInsert(remappedClasses(YES), oldcls, newcls);
    assert(!old);
}

// 獲取cls的重映射類
// 注意:當remappedClasses爲空或哈希表中不存在`cls`關鍵字,是返回`cls`自己,不然返回`cls`重映射後的類
static Class remapClass(Class cls)
{
    runtimeLock.assertLocked();

    Class c2;

    if (!cls) return nil;

    NXMapTable *map = remappedClasses(NO);
    if (!map  ||  NXMapMember(map, cls, (void**)&c2) == NX_MAPNOTAKEY) {
        return cls;
    } else {
        return c2;
    }
}

// 對Class的指針的重映射,返回時傳入的clsref將 指向*clsref重映射後的類
static void remapClassRef(Class *clsref)
{
    runtimeLock.assertLocked();

    Class newcls = remapClass(*clsref);    
    if (*clsref != newcls) *clsref = newcls;
}
複製代碼

最後概括出如下結論:

  • Future class 重映射返回新的類,保存在remappedClasses全局哈希表中;
  • 普通類重映射返回類自己;
  • 重映射的真正的目的是支持類的懶加載,懶加載類暫存爲 future class 只記錄類名及 future class 屬性,在調用readClass才正式載入類數據。

3.1.3 realized class

調用readClass(...)讀取類數據只是載入了類的class_ro_t靜態數據,所以仍須要進一步配置objc_classclass_rw_t結構體的數據。這個過程爲 class realizing,姑且稱之爲認識類。具體包括:

  • 配置class_rw_tRW_REALIZEDRW_REALIZING位;
  • 根據class_ro_tRO_META位的值,配置class_rw_tversion
  • 由於靜態載入的父類、元類有可能被重映射,所以要保證類的父類、元類完成class realizing;
  • 配置class_rw_tsuperclass
  • 初始化objc_classisa指針;
  • 配置ivarLayoutinstanceSizeinstanceStart。該步驟很是重要,新版本 runtime 支持 non-fragile instance variables,類的instanceStartinstanceSize會根據父類的instanceSize動態調整,且須要按 WORD 對齊(TODO:後續在獨立的文章中詳細介紹);
  • 配置class_rw_tRO_HAS_CXX_STRUCTORSRO_HAS_CXX_DTOR_ONLYRW_FORBIDS_ASSOCIATED_OBJECTS
  • 添加子類/根類;
  • class_ro_t中的基本方法列表、屬性列表、協議列表,類的分類(category)中的方法列表等信息添加到class_rw_t中(TODO:後續在獨立的文章中詳細介紹);

實現 class realizing 的代碼主要在static Class realizeClassWithoutSwift(Class cls)函數中,只須要知道其大體過程便可。具體代碼及註釋以下:

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;
    if (cls->isRealized()) return cls;
    assert(cls == remapClass(cls));  // 傳入的類必須不存在於remappedClasses全局哈希表中

    ro = (const class_ro_t *)cls->data();
    if (ro->flags & RO_FUTURE) {
        // 若爲future class,則cls的rw指向class_rw_t結構體,ro指向class_ro_t結構體,維持原狀
        rw = cls->data();
        ro = cls->data()->ro;
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // 普通類,則須要爲rw分配內存,並將ro指針指向 傳入的cls->data()所指向的內存空間
        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->chooseClassArrayIndex();

    // 父類realizing
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
    // 元類realizing
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
 
#if SUPPORT_NONPOINTER_ISA
    // 配置RW_REQUIRES_RAW_ISA位。可忽略。
    bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
    bool rawIsaIsInherited = false;
    static bool hackedDispatch = false;

    if (DisableNonpointerIsa) {
        instancesRequireRawIsa = true;
    }
    else if (!hackedDispatch  &&  !(ro->flags & RO_META)  &&  
             0 == strcmp(ro->name, "OS_object")) 
    {
        hackedDispatch = true;
        instancesRequireRawIsa = true;
    }
    else if (supercls  &&  supercls->superclass  &&  
             supercls->instancesRequireRawIsa()) 
    {
        instancesRequireRawIsa = true;
        rawIsaIsInherited = true;
    }
    
    // 配置RW_REQUIRES_RAW_ISA位
    if (instancesRequireRawIsa) {
        cls->setInstancesRequireRawIsa(rawIsaIsInherited);
    }
#endif

    // 因爲存在class remapping的可能性,所以須要更新父類及元類
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

    // 調整ivarLayout
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // 調整instanceSize
    cls->setInstanceSize(ro->instanceSize);

    // 忽略
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    
    // 忽略
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // 添加子類/根類
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // rw中須要保存ro中的一些數據,例如ro中的基礎方法列表、屬性列表、協議列表
    // rw還須要載入分類的方法列表
    methodizeClass(cls);

    return cls;
}

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()) {
        // 根元類特殊處理
        addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
    }

    // 將分類中的方法列表添加到rw的方法列表中
    category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
    attachCategories(cls, cats, false /*don't flush caches*/); if (PrintConnecting) { if (cats) { for (uint32_t i = 0; i < cats->count; i++) { _objc_inform("CLASS: attached category %c%s(%s)", isMeta ? '+' : '-', cls->nameForLogging(), cats->list[i].cat->name); } } } if (cats) free(cats); } 複製代碼

最後總結,截止至完成 class realizing,類的加載過程大體以下圖所示。其中future class列是懶加載類(future class)的流程,通過了「添加懶加載類->加載懶加載類信息->懶加載類重映射->認識懶加載類」四步;normal class列是普通的非懶加載類的加載流程,只通過「加載類信息->認識類」兩個步驟。

截止至完成class realizing類的加載過程.jpg

類完成 class realizing 後,還須要執行類及分類中的load()方法,最後在程序運行過程當中第一次調用類的方法時(實現邏輯在IMP lookUpImpOrForward(...)函數中)觸發isInitialized()檢查,若未初始化,則須要先執行類的initialize()方法。至此,類正式加載完成。

注意:最後的 class initializing 嚴格意義上應該不屬於類的加載過程,能夠將其歸爲獨立的類初始化階段。類的加載在load()方法執行後就算是完成了。

3.2 基本狀態相關行爲

objc_class結構體中類的基本狀態查詢的函數代碼以下。注意Class getMeta()獲取元類時:對於元類,getMeta()返回的結果與ISA()返回的結果不相同,對於非元類,二者則是相同的。

bool isARC() {
        return data()->ro->flags & RO_IS_ARC;
    }

    bool isMetaClass() {
        assert(this);
        assert(isRealized());
        return data()->ro->flags & RO_META;
    }

    bool isMetaClassMaybeUnrealized() {
        return bits.safe_ro()->flags & RO_META;
    }

    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }

    bool isRootClass() {
        return superclass == nil;
    }
    bool isRootMetaclass() {
        return ISA() == (Class)this;
    }

    const char *mangledName() { 
        assert(this);

        if (isRealized()  ||  isFuture()) {
            return data()->ro->name;
        } else {
            return ((const class_ro_t *)data())->name;
        }
    }
    
    const char *demangledName();
    const char *nameForLogging();
複製代碼

3.3 內存分配相關行爲

根據類的信息構建對象時,須要根據類的繼承鏈上的全部成員變量的內存佈局爲成員變量數據分配內存空間,分配內存空間的大小固定的,並按 WORD 對齊,調用size_t class_getInstanceSize(Class cls)實際是調用了objc_class結構體的uint32_t alignedInstanceSize()函數。

成員變量在實例內存空間中偏移量一樣也是固定的,一樣也是按 WORD 對齊。實例的第一個成員變量內存空間的在實例空間中的偏移量,實際是經過調用objc_class結構體的uint32_t alignedInstanceStart()函數獲取。

objc_class結構體中涉及內存分配的函數代碼以下:

// 類的實例的成員變量起始地址可能不按WORD對齊
    uint32_t unalignedInstanceStart() {
        assert(isRealized());
        return data()->ro->instanceStart;
    }

    // 配置類的實例的成員變量起始地址按WORD對齊
    uint32_t alignedInstanceStart() {
        return word_align(unalignedInstanceStart());
    }

    // 類的實例大小可能由於ivar的alignment值而不按WORD對齊
    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        return data()->ro->instanceSize;
    }

    // 配置類的實例大小按WORD對齊
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

    // 獲取類的實例大小
    size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes. (TODO:不懂爲啥)
        if (size < 16) size = 16;
        return size;
    }

    // 配置類的實例大小
    void setInstanceSize(uint32_t newSize) {
        assert(isRealized());
        if (newSize != data()->ro->instanceSize) {
            assert(data()->flags & RW_COPIED_RO);
            *const_cast<uint32_t *>(&data()->ro->instanceSize) = newSize;
        }
        bits.setFastInstanceSize(newSize);
    }

複製代碼

4、對象

對象的數據結構是objc_object結構體。objc_object僅包含一個isa_t類型的isa指針,和<objc/runtime>定義的objc_object有所不一樣,後者的isa指針是Class(指向objc_class結構體)。這是由於新版本 runtime 支持非指針類型isa結構,非指針類型isa再也不是指向Class的指針而是64位二進制位域,僅使用其中一部分位域保存對象的類的地址,其餘位賦予特殊意義主要用於協助對象內存管理。

objc_object包含的方法主要有如下幾類:

  • isa操做相關,isa指向對象類型,在控制對象構建、對象成員變量訪問、對象消息響應,對象內存管理方面有十分關鍵的做用,將在 4.1 中詳細介紹isa
  • 關聯對象(associated object)相關;
  • 對象弱引用相關,對象釋放後須要通知弱引用自動置nil,所以對象須要知曉全部弱引用的地址;
  • 引用計數相關,支持對象的引用計數(reference count)管理;
  • dealloc相關,對象析構相關,主要是釋放對關聯對象的引用;
  • side table 相關,side table 是 runtime 管理對象內存的核心數據結構,包含對象內存引用計數信息、對象的弱引用信息等關鍵數據(TODO:後續在獨立文章中介紹);
  • 支持非指針類型isa相關;

對象的定義代碼以下:

struct objc_object {
private:
    isa_t isa;

public:

    // 獲取對象類型,創建在對象不是tagged pointer的假設上
    Class ISA();

    // 獲取對象類型,對象能夠是tagged pointer
    Class getIsa();

    // 初始化isa
    void initIsa(Class cls /*nonpointer=false*/);
    void initClassIsa(Class cls /*nonpointer=maybe*/);
    void initProtocolIsa(Class cls /*nonpointer=maybe*/);
    void initInstanceIsa(Class cls, bool hasCxxDtor);

    // 設置isa指向新的類型
    Class changeIsa(Class newCls);

    // 對象isa是否爲非指針類型
    bool hasNonpointerIsa();

    // TaggedPointer相關,忽略
    bool isTaggedPointer();
    bool isBasicTaggedPointer();
    bool isExtTaggedPointer();

    // 對象是不是類
    bool isClass();

    // 對象關聯對象相關
    bool hasAssociatedObjects();
    void setHasAssociatedObjects();

    // 對象弱引用相關
    bool isWeaklyReferenced();
    void setWeaklyReferenced_nolock();

    //對象是否包含 .cxx 構造/析構函數
    bool hasCxxDtor();

    // 引用計數相關
    id retain();
    void release();
    id autorelease();

    // 引用計數相關的實現
    id rootRetain();
    bool rootRelease();
    id rootAutorelease();
    bool rootTryRetain();
    bool rootReleaseShouldDealloc();
    uintptr_t rootRetainCount();

    // dealloc的實現
    bool rootIsDeallocating();
    void clearDeallocating();
    void rootDealloc();

private:
    void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);

    id rootAutorelease2();
    bool overrelease_error();

#if SUPPORT_NONPOINTER_ISA
    // 支持非指針類型isa
    id rootRetain(bool tryRetain, bool handleOverflow);
    bool rootRelease(bool performDealloc, bool handleUnderflow);
    id rootRetain_overflow(bool tryRetain);
    bool rootRelease_underflow(bool performDealloc);

    void clearDeallocating_slow();

    void sidetable_lock();
    void sidetable_unlock();

    void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
    bool sidetable_addExtraRC_nolock(size_t delta_rc);
    size_t sidetable_subExtraRC_nolock(size_t delta_rc);
    size_t sidetable_getExtraRC_nolock();
#endif

    // Side-table 相關操做
    bool sidetable_isDeallocating();
    void sidetable_clearDeallocating();

    bool sidetable_isWeaklyReferenced();
    void sidetable_setWeaklyReferenced_nolock();

    id sidetable_retain();
    id sidetable_retain_slow(SideTable& table);

    uintptr_t sidetable_release(bool performDealloc = true);
    uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);

    bool sidetable_tryRetain();

    uintptr_t sidetable_retainCount();
#if DEBUG
    bool sidetable_present();
#endif
};
複製代碼

Tagged Pointer:對於一個對象引用(指針),通常狀況下該引用的值爲對象的內存地址,而tagged pointer則直接在地址中寫入對象的類和數據。

4.1 對象的 isa

objc_objectisa主要用於標記對象的類型。新版本 runtime 的isa支持兩種形式:指針類型、非指針類型。前者簡單指向對象的類。後者爲64位二進制位域,固然其中也包括對象的類的地址,其餘位域都有其特殊含義。爲支持兩種形式,runtime 使用isa_t聯合體保存對象的isa

注意:Union 聯合體的成員之間共享內存空間。以isa_t爲例,cls成員和bits成員雖然不一樣,可是二者的值實際在任什麼時候候都是一致的。例如,isa.class = [NSString class]指定了cls指向NSString類的內存地址,此時查看isa.bits會發現其值爲NSString類的內存地址;反之,isa.bits = 0xFF,則isa.class的值也變爲255

4.1.1 isa_t 聯合體

isa_t聯合體有兩個成員Class clsuintptr_t bits,二者共享8個字節的內存空間(64位機)。如下爲isa_t的源代碼,刪除了其中 x86_64 及其餘架構下的代碼。

union isa_t 
{
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;

// 須要編譯選項支持非指針類型isa
#if SUPPORT_NONPOINTER_ISA

# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
    struct {
        uintptr_t indexed           : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
    };
# endif

// SUPPORT_NONPOINTER_ISA
#endif
};
複製代碼

下面是 arm64 架構下isa_tbits的位域分佈圖示,左高位右低位。下面對各個位域的解析中有屢次說起 side table(TODO:後續獨立文章介紹),該結構用於對內存中的全部 Objective-C 對象進行統一的內存管理,其中最重要的是對象內存計數管理、對象弱指針管理。

  • indexed:洋紅區域右起第1位。0表示isa爲指針類型,存儲類的地址;1表示isa爲非指針類型。之因此使用最低位區分isa類型,是由於當isaClass時,其本質是指向objc_class結構體首地址的指針,因爲objc_class一定按WORD對齊,即地址一定是8的整數倍,所以指針類型的isa的末尾3位一定全爲0
  • has_assoc:洋紅區域右起第2位。標記對象是否存在關聯對象;
  • has_cxx_dtor:洋紅區域右起第3位。標記對象是否存在cxx語系的析構函數。使用指針類型isa的對象,該標記保存在 side table 中;
  • shiftcls:紅色區域共33位。保存類的虛擬內存地址,標記對象的類型(核心數據);
  • magic:黃色區域共6位。用於非指針類型的isa校驗,arm64架構下這6位爲固定值0x1a
  • weakly_referenced:青色區域右起第1位。標記對象是否被弱引用。使用指針類型isa的對象,該標記保存在 side table 中;
  • deallocating:青色區域右起第2位。標記對象是否已執行析構。使用指針類型isa的對象,該標記保存在 side table 中;
  • has_sidetable_rc:青色區域右起第3位。標記是否聯合 side table 保存該對象的引用計數;
  • extra_rc:綠色區域共19位。記錄對象引用計數,在has_sidetable_rc1時,須要聯合 side table 才能獲取對象的確切引用計數;

isa的各個位域.jpg

注意:MSB是Most Significant Bit指最高有效位,extra_rc須要處理上溢出狀況所以爲MSB,LSB是Least Significant Bit,indexed位用來判斷isa指針的類型所以爲LSB。

4.1.2 對象的 isa 相關操做

4.1.2.1 isa 的構建

對象構建時,須要直接或間接調用objc_objectinitIsa(Class cls, bool nonpointer, bool hasCxxDtor)構建isa,其餘initIsa方法均在內部調用了該方法。其中cls參數表示對象的類,nonpointer表示是否構建非指針類型isahasCxxDtor表示對象是否存在cxx語系析構函數。

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    assert(!isTaggedPointer()); 
    
    if (!nonpointer) {
        // 構建非指針類型isa
        isa.cls = cls;
    } else {
        // 構建非指針類型isa
        assert(!DisableNonpointerIsa);
        assert(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
        assert(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        newisa.has_cxx_dtor = hasCxxDtor;

        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        // 當前主流機型通常會運行到這個邏輯分支
        newisa.bits = ISA_MAGIC_VALUE;  // magic設置爲0xA1,index設置爲1
        newisa.has_cxx_dtor = hasCxxDtor;

        // shiftcls位域保存對象的類的地址,注意最低3位不須要保存,由於一定是全0
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // 指向新的newisa
        isa = newisa;
    }
}
複製代碼
4.1.2.2 isa 的應用

非指針類型isa實際是將 side table 中部份內存管理數據(包括部份內存引用計數、是否包含關聯對象標記、是否被弱引用標記、是否已析構標記)轉移到isa中,從而減小objc_object中內存管理相關操做的 side table 查詢數量。objc_object中關於狀態查詢的方法,大多涉及到isa的位操做。方法數量有點多不一一列舉,本節只以弱引用相關查詢爲例。

  • isWeaklyReferenced()用於查詢對象是否被弱引用。當對象isa爲非指針類型時,直接返回isa.weakly_referenced,不然須要調用sidetable_isWeaklyReferenced ()從 side table 中查詢結果;

  • setWeaklyReferenced_nolock()用於設置對象是否被弱引用。當對象isa爲非指針類型時,僅需將weakly_referenced位置爲1,不然須要調用sidetable_setWeaklyReferenced_nolock()從 side table 中查詢結果並寫入。

inline bool
objc_object::isWeaklyReferenced()
{
    assert(!isTaggedPointer());
    if (isa.nonpointer) return isa.weakly_referenced;
    else return sidetable_isWeaklyReferenced();
}


inline void
objc_object::setWeaklyReferenced_nolock()
{
// 源代碼設置weakly_referenced過程比較繁雜
 retry:
    isa_t oldisa = LoadExclusive(&isa.bits);
    isa_t newisa = oldisa;
    if (slowpath(!newisa.nonpointer)) {
        ClearExclusive(&isa.bits);
        sidetable_setWeaklyReferenced_nolock();
        return;
    }
    if (newisa.weakly_referenced) {
        ClearExclusive(&isa.bits);
        return;
    }
    newisa.weakly_referenced = true;
    if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
}
複製代碼

4.2 對象的構建

對象的構建本質上都經過調用_class_createInstanceFromZone(...)函數實現,其中最關鍵的傳入參數是Class類型的cls,含義是構建類爲cls的對象。代碼看起來挺長,實際上僅包含兩個操做:

  • 爲對象分配cls->instanceSize()大小的內存空間;
  • 構建對象的isa
static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    if (!cls) return nil;

    assert(cls->isRealized());

    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();

    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone  &&  fast) {
        // ----------- 邏輯分支1 ----------- //
        // 1.1 分配對象內存
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        
        // 1.2 構建對象isa
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        // ----------- 邏輯分支2 ----------- //
        // 2.1 分配對象內存
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // 2.2 構建對象isa
        obj->initIsa(cls);
    }

    // 若存在cxx語系構造函數,則調用。可忽略
    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}
複製代碼

4.3 對象的析構

對象的析構調用對象的rootDealloc()方法,源代碼雖然不不少可是整個過程通過了幾個函數。總結對象析構所須要的操做以下:

  • 釋放對關聯對象的引用;
  • 清空 side table 中保存的該對象弱引用地址、對象引用計數等內存管理數據;
  • 釋放對象佔用的內存;
inline void
objc_object::rootDealloc()
{
    if (isTaggedPointer()) return;  // fixme necessary?

    if (fastpath(isa.nonpointer  &&  
                 !isa.weakly_referenced  &&  
                 !isa.has_assoc  &&  
                 !isa.has_cxx_dtor  &&  
                 !isa.has_sidetable_rc))
    {
        assert(!sidetable_present());
        // 釋放對象佔用內存
        free(this);
    } 
    else {
        object_dispose((id)this);
    }
}

id 
object_dispose(id obj)
{
    if (!obj) return nil;

    objc_destructInstance(obj);    
    // 釋放對象佔用內存
    free(obj);

    return nil;
}

void *objc_destructInstance(id obj) 
{
    if (obj) {
        bool cxx = obj->hasCxxDtor();
        bool assoc = obj->hasAssociatedObjects();

        // 若存在cxx語系析構函數,則調用。可忽略
        if (cxx) object_cxxDestruct(obj);

        // 釋放關聯對象
        if (assoc) _object_remove_assocations(obj);

        // 清空side table中保存的該對象的弱引用地址、及引用計數等內存管理數據
        obj->clearDeallocating();  // 可忽略實現細節
    }

    return obj;
}
複製代碼

5、總結

  • Runtime 中對象用objc_object實現,用isa_t類型佔用8字節內存空間的isa成員指向對象的類,新版本 runtime 的isa還保存了對象引用計數、是否已析構、是否被弱引用、是否存在關聯對象等對象內存管理的關鍵數據;

  • Runtime 中類用objc_class實現,類也是一個對象,objc_classisa指向類的元類,元類的isa指向根元類,根元類的isa指向根元類自身,該條件能夠用於判斷類是否爲根元類;

  • objc_classsuperclass成員指向類的父類,用於組織類的繼承鏈;

  • objc_class的數據保存在bits成員的有效位域指向的內存空間中,類的編譯時決議數據保存在class_ro_t結構體中,運行時決議數據保存在class_rw_t結構體中,class_ro_tclass_rw_tflags成員用於標記類的狀態,數據總入口爲class_rw_t

  • 類存在懶加載機制,懶加載類先標記爲 future class,正式加載 future class 數據須要調用readClass(...)方法,對 future class 進行重映射(remapping);

  • 從鏡像加載的類因爲只包含編譯時決議數據,所以bits成員指向class_ro_t數據結構。必須通過 class realizing,構建類的class_rw_t數據,調整類的instanceSizeinstanceStartivarLayout(爲了支持 non-fragile instance variables),以及將class_ro_t中的基本方法列表、屬性列表、協議列表,類的分類(category)中的方法列表等信息添加到class_rw_t中;

  • 類的成員變量、方法列表、屬性列表、分類的實現及加載、對象內存管理涉及的 side table 將在後續獨立文章中詳細介紹。

相關文章
相關標籤/搜索