isa的初始化
isa的初始化流程代碼
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
assert(!cls->instancesRequireRawIsa());
assert(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
// -> initIsa
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!nonpointer) {
// 如果單純是個指針,就將類傳過來的類賦值給isa的cls
isa.cls = cls;
} else {
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;
newisa.has_cxx_dtor = hasCxxDtor;// 標(biāo)記是否存在c++ 析構(gòu)函數(shù)
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
isa = newisa;
}
}
問題解釋:為什么賦值了cls之后就不賦值bits了?
if (!nonpointer) {
`isa.cls = cls;`
}else{
...
`newisa.bits = ISA_INDEX_MAGIC_VALUE;`
...
}
原因是:isa是一個命名為isa_t的聯(lián)合體,聯(lián)合體中Class cls;和uintptr_t bits;是 互斥的。
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初始化,是進(jìn)行一系列參數(shù)賦值,如圖所示:

timg.jpeg
通過代碼
isa_t newisa(0);和isa = newisa;可以知道,isa是一個isa_t類型的結(jié)構(gòu)體
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
};
其中在位域 struct { ISA_BITFIELD; // defined in isa.h };中可以查到 isa_t的結(jié)構(gòu)代碼(分為arm64和x86兩種)如下:
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 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
# define ISA_BITFIELD \
uintptr_t nonpointer : 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)
isa_t結(jié)構(gòu)代碼的解釋
- nonpointer 表示是否對isa指針開啟優(yōu)化(我們現(xiàn)在的都是開啟了優(yōu)化),值0:純isa指針 值1:不止是類對象地址,isa中還包含了類信息,對象的引用計數(shù)等;
- has_assoc 是否有關(guān)聯(lián)對象, 值0 沒有 值1 有;
- has_cxx_dtor 該對象是否有c++或者objc析構(gòu)函數(shù),如果有析構(gòu)函數(shù),先走析構(gòu)邏輯,沒有就更快的釋放對象;
- shiftcls存儲類指針的值,開啟指針優(yōu)化的時候 在arm64架構(gòu)中有33位存儲類指正
- magic用于調(diào)試器判斷當(dāng)前對象是真的對象還是沒有初始化的空間
- weakly_referenced 標(biāo)志對象是否指向或者曾經(jīng)指向一個ARC的弱變量,沒有弱引用的對象可以更快的釋放
- deallocating 標(biāo)志對象是否正在釋放
- has_sidetable_rc 當(dāng)對象引用計數(shù)大于10時,則需要借用該變量存儲進(jìn)位
- extra_rc表示對象的引用計數(shù)的值,實(shí)際上是引用計數(shù)減1.
例如 如果對象的引用計數(shù)位10 則extra_rc為9 ,若引用計數(shù)大于10 ,則需要用到上面的has_sidetable_rc。
對象的第一個屬性必然是isa
驗(yàn)證方法:
- NSObject 中定義第一屬性就是isa
Class isa
- NSObject 中定義第一屬性就是isa
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
- 2.isa必然關(guān)聯(lián)一個class isa <-> class
根據(jù)newisa.shiftcls = (uintptr_t)cls >> 3;進(jìn)行二進(jìn)制位移后,后面相同,如圖截屏2019-12-22下午11.22.15.png
isa 指向分析
- 通過lldb進(jìn)行調(diào)試可以發(fā)現(xiàn)
初始化類
LGPerson *person = [LGPerson alloc];
根據(jù)(Class)(isa.bits & ISA_MASK);lldb調(diào)試如下
(lldb) x/4gx person
0x100f3b150: 0x001d8001000023b5 0x0000000000000000
0x100f3b160: 0x0000000000000000 0x0000000000000000
(lldb) p/x LGPerson.class
(Class) $1 = 0x00000001000023b0 LGPerson
(lldb) p/x 0x00000001000023b0 & 0x0000000ffffffff8
(long) $2 = 0x00000001000023b0
(lldb) po 0x00000001000023b0
LGPerson
通過調(diào)試可以得出:isa 從類的實(shí)例對象person指向了類LGPerson。
繼續(xù)調(diào)試,打印LGPerson首地址也是LGPerson
(lldb) x/4gx LGPerson.class
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x00000001003da290 0x0000000000000000
(lldb) po 0x1000023b0
LGPerson
根據(jù)(Class)(isa.bits & ISA_MASK);找到最終類為NSObject
(lldb) x/4gx LGPerson.class
0x1000023b0: 0x001d800100002389 0x0000000100b37140
0x1000023c0: 0x00000001003da290 0x0000000000000000
(lldb) p/x 0x001d800100002389 & 0x0000000ffffffff8
(long) $8 = 0x0000000100002388
(lldb) po 0x0000000100002388
LGPerson
(lldb) x/4gx 0x0000000100002388
0x100002388: 0x001d800100b370f1 0x0000000100b370f0
0x100002398: 0x000000010104ee30 0x0000000400000007
(lldb) p/x 0x001d800100b370f1 & 0x0000000ffffffff8
(long) $10 = 0x0000000100b370f0
(lldb) po 0x0000000100b370f0
NSObject
(lldb) x/4gx 0x0000000100b370f0
0x100b370f0: 0x001d800100b370f1 0x0000000100b37140
0x100b37100: 0x000000010123d3e0 0x0000000400000007
(lldb) p/x 0x001d800100b370f1 & 0x0000000ffffffff8
(long) $12 = 0x0000000100b370f0
(lldb) po 0x0000000100b370f0
NSObject
通過上圖調(diào)試可以完美驗(yàn)證isa的走位如圖所示
isa 指向圖

1129392-2fcd94dced3798a2.png
- 虛線代表了isa的走位。實(shí)例對象->類->元類->根元類->根根元類(根元類本身)。
- 實(shí)線代表著繼承。
總結(jié)
- 實(shí)例對象的isa指向的是類;
- 類的isa指向的元類;
- 元類isa指向根元類;
- 根元類isa指向自己;
- NSObject的父類是nil,根元類的父類是NSObject。
