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 是什么東西呢?
其中 isa 是 isa_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ù)據

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_object 的 isa 中找到對象所屬的類 ( objc_class )然后在所屬的類中搜索相對應得方法。如果沒有找到那么就根據 objc_class 的 superclass 找到父類,在父類中搜索相對應的方法。
元類
我們上面提到,本質上 OC 中的類也是對象,它也是某個類的實例,這個類我們稱之為元類(metaclass)。
因此,我們也可以通過調用類方法,比如 [NSObject new],給類對象發(fā)送消息。同樣的,類對象能否響應這個消息也要通過 isa 找到類對象所屬的類(元類)才能知道。也就是說,實例方法是保存在類中的,而類方法是保存在元類中的。
那元類也是對象嗎?是的話那它又是什么類的實例呢?是的,沒錯,元類也是對象(元類對象),元類也是某個類的實例,這個類我們稱之為根元類(root metaclass)。不過,有一點比較特殊,那就是所有的元類所屬的類都是同一個根元類(當然根元類也是元類,所以它所屬的類也是根元類,即它本身)。根元類指的就是根類的元類,具體來說就是根類 NSObject 對應的元類*。
因此,理論上我們也可以給元類發(fā)送消息,但是 OC 傾向于隱藏元類,不想讓大家知道元類的存在。元類是為了保持 OC 對象模型在設計上的完整性而引入的,比如用來保存類方法等,它主要是用來給編譯器使用的。
可以用一張圖概括
