isa 結(jié)構(gòu)回顧
上篇 對(duì)象 與 isa 的關(guān)系 我們得知了 isa 連接了 對(duì)象 和 類(對(duì)象的 isa 指向了 對(duì)象所屬的類對(duì)象)
一. 對(duì)象
當(dāng)我們調(diào)用 obj.class
的時(shí)候?yàn)槭裁磿?huì)返回 類相關(guān)的信息呢? 上篇我們知道 類相關(guān)的信息是存儲(chǔ)在對(duì)象的 isa 中的,那我們是不是可以猜測(cè) obj.class 對(duì) isa 進(jìn)行了相關(guān)的操作 取出了 isa
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
inline Class
objc_object::getIsa()
{
if (fastpath(!isTaggedPointer())) return ISA();
extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
uintptr_t slot, ptr = (uintptr_t)this;
Class cls;
slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
cls = objc_tag_classes[slot];
if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
cls = objc_tag_ext_classes[slot];
}
return cls;
}
inline Class
objc_object::ISA()
{
ASSERT(!isTaggedPointer());
#if SUPPORT_INDEXED_ISA
if (isa.nonpointer) {
uintptr_t slot = isa.indexcls;
return classForIndex((unsigned)slot);
}
return (Class)isa.bits;
#else
return (Class)(isa.bits & ISA_MASK);
#endif
}
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# else
# error unknown architecture for packed isa
# endif
可以看到 isa 最終返回的是 (Class)(isa.bits & ISA_MASK)
; 在 isa.h
可以看到ISA_MASK的定義
所以 當(dāng)我們獲取 class 的時(shí)候 本質(zhì)是通過(guò) isa的內(nèi)存地址 進(jìn)行內(nèi)存運(yùn)算,強(qiáng)轉(zhuǎn) Class 類型 獲得的
二. isa 流程圖
我們知道 對(duì)象的 isa 指向了類捶枢,那么類的 isa 指向的又是? 借用一張 蘋果官方的 isa 流程圖
我們現(xiàn)在來(lái)驗(yàn)證這幅圖
TObject *obj = [TObject new];
斷點(diǎn)斷住 我們開始打印
通過(guò)位移運(yùn)算 驗(yàn)證了
obj 的 isa
-> TObject(類)
注意 兩個(gè) TObject
并不是同一個(gè)地址,所以不是同一個(gè)東西
所以這也就是說(shuō) TObject(類)
-> TObject(元類)
我們可以看到元類的isa
經(jīng)過(guò)內(nèi)存計(jì)算拳恋,打印出的是NSObjct
也就是我們的根元類
我們可以看到 根元類的地址 和 根元類 isa 指向的地址是同一片地址
所以 也就有了 TObject(元類)
-> NSObject(根元類)
-> NSObject(根元類) 自己
- 總結(jié)
-
obj對(duì)象
->TObect類
->TObject元類
->NSObject根元類
->NSObject根元類自己
也就有了 官方圖片中的虛線部分
類的結(jié)構(gòu)體
在前面我們知道 isa 是一個(gè) isa_t 的結(jié)構(gòu)體
typedef struct objc_class *Class;
typedef struct objc_object *id;
namespace {
struct SideTable;
};
#include "isa.h"
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
之前講到 isa_t
的結(jié)構(gòu)體中 cls
存儲(chǔ)的對(duì)象所屬的類, 所以是Class
又是 objc_class
的結(jié)構(gòu)體指針,也就是說(shuō)類的本質(zhì) 在底層 是 objc_class
然而 objc_class
也繼承于 objc_object
, 也就是說(shuō)類也是一個(gè)對(duì)象裆泳,所以第一個(gè)指針也是isa
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() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
}
截取了一部分源碼
superclass
父類
所以 第二段地址就是 父類
我們現(xiàn)在開始打印
類的繼承關(guān)系
- 定義繼承關(guān)系
TObjectChildren
->TObject
->NSObject
-
驗(yàn)證類的繼承關(guān)系
通過(guò)驗(yàn)證得知 類的繼承關(guān)系 TObjectChildren
-> TObject
-> NSObject
-> nil
元類的繼承關(guān)系
那么元類的繼承關(guān)系又是什么樣的呢
我們用上面類的地址虹蒋,獲取相應(yīng)的元類
// TObjectChildren 的內(nèi)存信息
(lldb) x/4gx obj.class
0x1000082c0: 0x0000000100008298 0x0000000100008270
0x1000082d0: 0x0003000100620150 0x0004802100000000
// TObjectChildren 的元類
(lldb) p/x 0x0000000100008298 & 0x00007ffffffffff8ULL
(unsigned long long) $14 = 0x0000000100008298
(lldb) po 0x0000000100008298
TObjectChildren
// TObjectChildren 的元類的內(nèi)存地址
(lldb) x/4gx 0x0000000100008298
0x100008298: 0x00000002101712a0 0x0000000100008248 -> TObjectChildren 的元類的父類地址
0x1000082a8: 0x00010001007367f0 0x0002e03500000000
// TObject 內(nèi)存信息
(lldb) x/4gx 0x0000000100008270
0x100008270: 0x0000000100008248 0x00000002101712c8
0x100008280: 0x00000001a7e32f00 0x0000802100000000
// TObject 的元類
(lldb) p/x 0x0000000100008248 & 0x00007ffffffffff8ULL
(unsigned long long) $18 = 0x0000000100008248 -> TObject 的元類地址
(lldb) po 0x0000000100008248
TObject
----
我們從 0x0000000100008248 他是 TObjectChildren 的元類的父類的地址
在下方的TObject的元類地址與其相同
!F旯濉Q丁!2栽凇绝页!判定 TObjectChildren 的元類 繼承于 TObject 的元類
----
TObject 的元類內(nèi)存信息
(lldb) x/4gx 0x0000000100008248
0x100008248: 0x00000002101712a0 0x00000002101712a0->TObject 的元類的父類地址
0x100008258: 0x0001000100620300 0x0002e03500000000
//NSObject 內(nèi)存信息
(lldb) x/4gx 0x00000002101712c8
0x2101712c8: 0x00000002101712a0 0x0000000000000000
0x2101712d8: 0x000100010054a8b0 0x0001801000000000
// NSObject 元類
(lldb) p/x 0x00000002101712a0 & 0x00007ffffffffff8ULL
(unsigned long long) $20 = 0x00000002101712a0-> NSObject 的元類地址
(lldb) po 0x00000002101712a0
NSObject
----
我們從 0x00000002101712a0 他是 TObject 的元類的父類的地址
在下方的NSObject的元類地址與其相同
!<盘瘛P!3跞狻酷鸦!判定 TObject 的元類 繼承于 NSObject 的元類
----
// NSObject 元類的內(nèi)存信息
(lldb) x/4gx 0x00000002101712a0
0x2101712a0: 0x00000002101712a0 0x00000002101712c8
0x2101712b0: 0x0003000100620200 0x0003e03400000000
---
0x00000002101712c8 可以看出 NSObject的元類的父類地址 和 NSObject 的類地址相同
而 NSObject 的父類地址為空
所以 NSObejct的元類 繼承于 NSObject 類 -繼承于 nil
---
通過(guò)一步一步的調(diào)試我們得知
TObjectChildren元類
-> TObject元類
-> NSObject元類
-> NSObject類
-> nil