讀 Runtime 源碼:類與對象

以前只是看了很多博客,這次打算看一下源碼,并記錄下來。想到哪里就讀到哪里,寫到哪里。讀的代碼版本是:objc runtime 680,可以從這里下載

對象和類

首先在 objc-private.h文件中可以看到objc_object結(jié)構(gòu)體,這就是對對象的定義

struct objc_object {
private:
    isa_t isa;
}

objc-runtime-new.h中可以看到objc_class結(jié)構(gòu)體,這就是對類的定義,因?yàn)樗^承了objc_object,所以我把isa變量也加入到objc_class中來。

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

superclass指向該類的父類,NSObject的父類指向NULL

Cache

cache主要用于方法性能優(yōu)化,如果每發(fā)送一個(gè)消息都需要去方法表中去查找,當(dāng)方法很多的時(shí)候,查找是很耗力的,并且當(dāng) 存在繼承關(guān)系的時(shí)候,一個(gè)方法的查找鏈可能會很長。那么對使用過的方法進(jìn)行緩存,便于第二次查找,這樣節(jié)省的時(shí)間也是非常可觀的。

來看一下cache_t的具體代碼,在objc-runtime-new.h的第52行可以看到

struct cache_t {
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
}
  • mask:分配用來緩存bucket的總數(shù)
  • occupied:表明目前實(shí)際占用的緩存bucket的個(gè)數(shù)
  • _buckets:一個(gè)散列表,用來方法緩存,bucket_t類型,包含key以及方法實(shí)現(xiàn)IMP
struct bucket_t {
private:
    cache_key_t _key;
    IMP _imp;
}

class_data_bits_t

objc-runtime-new.h的 817 行可以看到結(jié)構(gòu)體中只有一個(gè)bits屬性來存儲類信息

struct class_data_bits_t {

    // Values are the FAST_ flags above.
    uintptr_t bits;
}

還有objc_class有注釋說

 // class_rw_t * plus custom rr/alloc flags

那我們看一下class_rw_t到底是什么,在objc-runtime-new.h的778行可以看到

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

Objc的類的屬性、方法、以及遵循的協(xié)議都放在class_rw_t中,下面對其中幾個(gè)關(guān)鍵詞進(jìn)行分析

class_ro_t

ro是一個(gè)指向常量的指針,存儲來編譯器決定了的屬性、方法和遵守協(xié)議

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

Method

Method存儲了方法名、類型信息、方法實(shí)現(xiàn)

struct method_t {
    SEL name;
    const char *types;
    IMP imp;
}

iVar

ivar代表類中的實(shí)例變量

struct ivar_t {
#if __x86_64__
    // *offset was originally 64-bit on some x86_64 platforms.
    // We read and write only 32 bits of it.
    // Some metadata provides all 64 bits. This is harmless for unsigned 
    // little-endian values.
    // Some code uses all 64 bits. class_addIvar() over-allocates the 
    // offset for their benefit.
#endif
    int32_t *offset;
    const char *name;
    const char *type;
    // alignment is sometimes -1; use alignment() instead
    uint32_t alignment_raw;
    uint32_t size;

    uint32_t alignment() const {
        if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
        return 1 << alignment_raw;
    }
};

說起來實(shí)例變量,就想到了對象的內(nèi)存結(jié)構(gòu),為了搞清楚,也做了一個(gè)測試,Person繼承于PeoplePeople繼承于NSObject

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        // 此處打斷點(diǎn)
        }
}

在注釋處打斷點(diǎn),然后在 Console 中輸入p *person,查看輸出結(jié)果,對象中會包含一個(gè)isa指針、類的實(shí)例變量,以及父類的實(shí)例變量

(lldb) p *person
(Person) $0 = {
  People = {
    NSObject = {
      isa = Person
    }
    _name = nil
  }
  _age = nil
}
(lldb) 

如果有理解錯(cuò)誤,望留言告知

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容