OC 中類的實質

id

id 可以表示任何 OC 中的對象,在 runtime 中對 id 是這么定義的

typedef struct objc_object *id;

所以 id 其實是一個指向 objc_object 結構體的指針類型,這也是為什么我們使用 OC 對象都需要添加 * ,而使用id對象的時候則不用。

既然 id 是指向結構體 objc_object的指針類型,那么 objc_object 是什么?
其實在 OC 中 objc_object 就是表示對象,所以在 OC 中任何對象都是 C 中的結構體。所以從這里看就知道為什么 id 可以表示任何對象類型了。

對象

在 OC 中對象是由結構體 objc_object 表示的,那么這個結構體的定義

struct objc_object {
private:
    isa_t isa;

public:
    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();

    // Optimized calls to retain/release methods
    id retain();
    void release();
    id autorelease();
};

為了方便觀看其中省略了一些其他的東西。

可以看到結構體中除了一個變量 isa 就沒有其他的變量了,還有一些其他常見的方法。所以其中的變量 isa 是什么東西呢?
其中 isaisa_t 類型的。

isa_t

找到 isa_t 的定義

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

    Class cls;
    uintptr_t bits;

#if SUPPORT_NONPOINTER_ISA

    // extra_rc must be the MSB-most field (so it matches carry/overflow flags)
    // indexed must be the LSB (fixme or get rid of it)
    // shiftcls must occupy the same bits that a real class pointer would
    // bits + RC_ONE is equivalent to extra_rc + 1
    // RC_HALF is the high bit of extra_rc (i.e. half of its range)

    // future expansion:
    // uintptr_t fast_rr : 1;     // no r/r overrides
    // uintptr_t lock : 2;        // lock for atomic property, @synch
    // uintptr_t extraBytes : 1;  // allocated with extra bytes

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

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
    struct {
        uintptr_t indexed           : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 8;
#       define RC_ONE   (1ULL<<56)
#       define RC_HALF  (1ULL<<7)
    };

# else
    // Available bits in isa field are architecture-specific.
#   error unknown architecture
# endif

// SUPPORT_NONPOINTER_ISA
#endif

};

可以看到 isa_t 是一個 union 類型,就是里面的成員都共用一個內存,大小則由最大的數(shù)據項決定。

那么為什么要用 union 類型呢?因為自從蘋果推出了 iPhone5s 采用了 64 位架構的 A7 雙核處理器,如果只用來存儲一個地址的話就會十分的浪費內存,所以蘋果引入了 TaggedPointer 。TaggedPointer 就是讓本來存儲內存地址的指針一部分用來存儲其他的數(shù)據,從而減少浪費。而對于 isa_t 就是使用一部分存儲所指向的類的地址,而另一部分則存儲像引用計數(shù)等其他數(shù)據。

從上面的關于 isa_t 的結構中可以看出很多的宏就是判斷是否支持 TaggedPoint 從而決定存儲方式。而判斷這個 isa 指針是否是 TaggedPoint 就是看指針的標志位是否為1。

#if SUPPORT_MSB_TAGGED_POINTERS
#   define TAG_MASK (1ULL<<63)
#else
#   define TAG_MASK 1
inline bool 
objc_object::isTaggedPointer() 
{
#if SUPPORT_TAGGED_POINTERS
    return ((uintptr_t)this & TAG_MASK);
#else
    return false;
#endif
}

所以回過頭來看 isa_t 其中除了儲存了一下相關數(shù)據

isa_t.png

Class

objc_object 結構體中有返回 Class 的函數(shù)

// ISA() assumes this is NOT a tagged pointer object
    Class ISA();

// getIsa() allows this to be a tagged pointer object
    Class getIsa();

函數(shù)中返回的 Class 類型就是 OC 中的類

那么這個 Class 是什么,

typedef struct objc_class *Class;

可以看到 Class是一個指向結構體 objc_class 的指針,找到結構體 objc_class 的定義

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_class 是繼承自 objc_object 的,所以在 OC 中類其實也是一個對象。
所以在 objc_class 中有的成員是

  • isa : 從 objc_object 中繼承得到
  • superclass : 指向父類
  • cache : 表示緩存的數(shù)據
  • bits : 里面存儲了類的方法和成員變量等

當調用實例方法的時候,那么就從 objc_objectisa 中找到對象所屬的類 ( objc_class )然后在所屬的類中搜索相對應得方法。如果沒有找到那么就根據 objc_classsuperclass 找到父類,在父類中搜索相對應的方法。

元類

我們上面提到,本質上 OC 中的類也是對象,它也是某個類的實例,這個類我們稱之為元類(metaclass)。

因此,我們也可以通過調用類方法,比如 [NSObject new],給類對象發(fā)送消息。同樣的,類對象能否響應這個消息也要通過 isa 找到類對象所屬的類(元類)才能知道。也就是說,實例方法是保存在類中的,而類方法是保存在元類中的。

元類也是對象嗎?是的話那它又是什么類的實例呢?是的,沒錯,元類也是對象(元類對象),元類也是某個類的實例,這個類我們稱之為根元類(root metaclass)。不過,有一點比較特殊,那就是所有的元類所屬的類都是同一個根元類(當然根元類也是元類,所以它所屬的類也是根元類,即它本身)。根元類指的就是根類的元類,具體來說就是根類 NSObject 對應的元類*。

因此,理論上我們也可以給元類發(fā)送消息,但是 OC 傾向于隱藏元類,不想讓大家知道元類的存在。元類是為了保持 OC 對象模型在設計上的完整性而引入的,比如用來保存類方法等,它主要是用來給編譯器使用的。

可以用一張圖概括

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容