在前面的OC底層-對(duì)象的alloc流程探究文章中管搪,alloc
的流程中,我們知道了OC
底層是通過(guò)initInstanceIsa
把我們的類cls
和isa
關(guān)聯(lián)起來(lái)馆铁,我們順著initInstanceIsa
去對(duì)今天的主角isa
一探究竟蚯瞧。
isa
是什么
isa
的類型
struct objc_object
private:
isa_t isa;
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
};
可以看到在objc_object
結(jié)構(gòu)體中,isa
是isa_t
類型颁虐。同時(shí)我們找到了isa_t
的定義。union
是聯(lián)合體卧须,這里我們的isa
顯然就是聯(lián)合體類型另绩。
union
和 struct
union
聯(lián)合體
(也稱共用體
)是由不同的數(shù)據(jù)類型組成儒陨,但其成員是互斥
的,所有的成員共占一段內(nèi)存
笋籽。聯(lián)合體采用了內(nèi)存覆蓋
技術(shù)蹦漠,同一時(shí)刻只能保存一個(gè)成員的值,如果對(duì)新的成員賦值车海,就會(huì)將原來(lái)成員的值覆蓋掉笛园。
- 優(yōu)點(diǎn):所有成員共用一段內(nèi)存,節(jié)省了內(nèi)存空間侍芝。
- 缺點(diǎn):包容性弱
struct
結(jié)構(gòu)體
是指把不同的數(shù)據(jù)組合成一個(gè)整體研铆,其成員是共存
的,成員不管是否使用州叠,都會(huì)分配內(nèi)存
棵红。
- 優(yōu)點(diǎn):存儲(chǔ)容量較大,包容性強(qiáng)咧栗,且成員之間不會(huì)相互影響窄赋。
- 缺點(diǎn):所有成員不管是否使用都分配內(nèi)存,比較浪費(fèi)內(nèi)存楼熄。
兩者的區(qū)別
- 內(nèi)存占用情況
- 聯(lián)合體的所有成員占用同一段內(nèi)存,成員互斥
- 結(jié)構(gòu)體的各個(gè)成員會(huì)占用不同的內(nèi)存浩峡,互相之間沒(méi)有影響
- 內(nèi)存分配大小
- 聯(lián)合體占用的內(nèi)存等于最大的成員占用的內(nèi)存
- 結(jié)構(gòu)體內(nèi)存等于最大的成員占用的內(nèi)存的整數(shù)倍
isa_t
的成員分析
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
};
# 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)
# else
# error unknown architecture for packed isa
# endif
通過(guò)聯(lián)合體
和結(jié)構(gòu)體
的特性可岂,不難分析出isa_t
采用聯(lián)合體
是基于內(nèi)存優(yōu)化
的考慮。isa指針
的內(nèi)存大小是8
字節(jié)翰灾,即64
bit缕粹,也就是64
位。isa
通過(guò)char+位域
的設(shè)計(jì)纸淮,巧妙的用二進(jìn)制的每一位來(lái)存儲(chǔ)和表示聯(lián)合體
成員的信息平斩。
isa_t
提供了的對(duì)應(yīng)的初始化方法isa_t() { }
和isa_t(uintptr_t value) : bits(value) { }
。isa_t
還提供了2個(gè)成員咽块。cls
和bits
绘面,以及一個(gè)結(jié)構(gòu)體定義的位域
,用來(lái)存儲(chǔ)類的信息。-
分析下
位域
中存儲(chǔ)的每個(gè)數(shù)據(jù)-
nonpointer
:表示是否對(duì)isa
指針開啟指針優(yōu)化侈沪。0:純isa指針揭璃,1:不止是類對(duì)象地址,isa 中包含了類信息亭罪、對(duì)象的引用計(jì)數(shù)等瘦馍。 -
has_assoc
:關(guān)聯(lián)對(duì)象標(biāo)志位,0沒(méi)有应役,1存在 -
has_cxx_dtor
:該對(duì)象是否有C++
或者Objc
的析構(gòu)器情组,如果有析構(gòu)函數(shù)燥筷,則需要做析構(gòu)邏輯,如果沒(méi)有院崇,則可以更快的釋放對(duì)象 -
shiftcls
:存儲(chǔ)類指針的值肆氓。開啟指針優(yōu)化的情況下,在arm64
架構(gòu)中有33
位用來(lái)存儲(chǔ)類指針亚脆。 -
magic
:用于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象還是沒(méi)有初始化的空間做院。 -
weakly_referenced
:標(biāo)志對(duì)象是否被指向或者曾經(jīng)指向一個(gè)ARC
的弱變量,沒(méi)有弱引用的對(duì)象可以更快釋放濒持。 -
deallocating
:標(biāo)志對(duì)象是否正在釋放內(nèi)存 -
has_sidetable_rc
:當(dāng)對(duì)象引用技術(shù)大于10
時(shí)键耕,則需要借用該變量存儲(chǔ)進(jìn)位 -
extra_rc
:當(dāng)表示該對(duì)象的引用計(jì)數(shù)值,實(shí)際上是引用計(jì)數(shù)值減1
柑营, 例如屈雄,如果對(duì)象的引用計(jì)數(shù)為10
,那么 extra_rc 為9
官套。如果引用計(jì)數(shù)大于10
酒奶, 則需要使用到下面的has_sidetable_rc
。
-
下面是x86_64
即macOS
平臺(tái)的isa_t
的成員存儲(chǔ)情況
[圖片上傳失敗...(image-519ad0-1600051522675)]
isa
做了什么
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
if (!nonpointer) {
isa = isa_t((uintptr_t)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;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
isa
成員賦值
- 和
alloc
一樣奶赔,我們?cè)?code>main.m中初始化MuPerson
惋嚎。由alloc
->_objc_rootAlloc
->callAlloc
->_objc_rootAllocWithZone
>_class_createInstanceFromZone
>initInstanceIsa
>initIsa
到上面這份核心代碼,打上斷點(diǎn)站刑,開始調(diào)試另伍,我們來(lái)看系統(tǒng)是如何對(duì)isa
的成員賦值。 - 初始化
isa
指針绞旅,這里依據(jù)nonpointer
來(lái)分別通過(guò)cls
和bits
對(duì)isa
初始化摆尝,也從側(cè)面驗(yàn)證了cls
和bits
互斥的原則,不能同時(shí)setter
因悲。 - 斷點(diǎn)過(guò)
isa_t newisa(0)
堕汞,對(duì)isa
初始化,到bits
賦值之前晃琳,我們通過(guò)p
打印newisa
結(jié)果如下
image - 斷點(diǎn)過(guò)
newisa.bits = ISA_MAGIC_VALUE
讯检,對(duì)bits
賦值之后,我們通過(guò)p
打印newisa
結(jié)果如下
image - 斷點(diǎn)到
isa = newisa
卫旱,對(duì)isa
賦值之后视哑,我們通過(guò)p
打印newisa
結(jié)果如下
image - 三次斷點(diǎn)
p
打印的newisa
結(jié)果做下對(duì)比如下
image
通過(guò)斷點(diǎn),我們清晰的看到isa
中的成員是如何一步一步的被賦值誊涯。
isa
和類
關(guān)聯(lián)
通過(guò)對(duì)isa
成員賦值分析挡毅,isa
和我們類
的關(guān)聯(lián)關(guān)鍵在于isa
中的成員shiftcls
,即newisa.shiftcls = (uintptr_t)cls >> 3
這句關(guān)鍵代碼。這里>> 3
是為了通過(guò)位運(yùn)算把cls
的信息準(zhǔn)確的放在shiftcls
的存儲(chǔ)位置上暴构。以這樣一種巧妙的方式跪呈,就把isa
和類
關(guān)聯(lián)起來(lái)段磨。
isa
是否和類關(guān)聯(lián)的驗(yàn)證
- 通過(guò)
isa
&ISA_MSAK
驗(yàn)證
(lldb) po cls
MuPerson
(lldb) po obj
<MuPerson: 0x1007076b0>
(lldb) x/4gx obj
0x1007076b0: 0x001d800100002255 0x0000000000000000
0x1007076c0: 0x0000000000000000 0x0000000000000000
(lldb) po 0x001d800100002255 & 0x00007ffffffffff8ULL
MuPerson
(lldb)
- 通過(guò)
object_getClass
驗(yàn)證
object_getClass
-> Class object_getClass
-> inline Class objc_object::getIsa()
-> inline Class objc_object::ISA()
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
}
- 通過(guò)
位運(yùn)算
驗(yàn)證
(lldb) p cls
(Class) $7 = MuPerson
(lldb) p/x cls
(Class) $8 = 0x0000000100002250 MuPerson
(lldb) p obj
(MuPerson *) $9 = 0x000000010192c440
(lldb) x/4gx obj
0x10192c440: 0x001d800100002255 0x0000000000000000
0x10192c450: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d800100002255 >> 3
(long) $11 = 0x0003b0002000044a
(lldb) p/x $11 << 20
(long) $12 = 0x0002000044a00000
(lldb) p/x $12 >> 17
(long) $13 = 0x0000000100002250
(lldb)
為什么設(shè)計(jì)isa
isa
走位流程圖
這里放上業(yè)界經(jīng)典isa
走位流程圖
- 子類的isa走位鏈:
子類對(duì)象 --> 子類 --> 子元類 --> NSObject(根元類) --> NSObject(根元類,即自己)
- 父類的isa走位鏈:
父類對(duì)象 --> 父類 --> 父元類 --> NSObject(根元類) --> NSObject(根元類耗绿,即自己)
- 類的繼承關(guān)系鏈:
子類 --> 父類 --> NSObject(根類)--> nil
- 元類的繼承關(guān)系鏈:
子元類 --> 父元類 --> 根元類 --> NSObject(根類)--> nil
objc_class
& objc_object
struct NSObject_IMPL {
Class isa;
};
typedef struct objc_class *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() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
///此處省略一萬(wàn)行
}
struct objc_object {
private:
isa_t isa;
///此處省略一萬(wàn)行
}
-
clang
編譯的mian.cpp
文件中苹支,NSObject
中的isa
在底層是由Class
定義的,其中Class
的底層編碼來(lái)自objc_class
類型误阻,而objc_class
又繼承于objc_object
债蜜。 -
OC
層面的根類NSObject
,初始化對(duì)象
就會(huì)有isa
特性,isa
的根源來(lái)自OC
底層的objc_object
究反。 - 所有的
類
是由objc_class
為模板生成的寻定,所有的對(duì)象
則是由objc_object
為模板生成的。
貫穿全局的isa
上面我們微觀
的分析了isa
做了什么精耐,isa
把對(duì)象
和類
關(guān)聯(lián)起來(lái)狼速。宏觀的看,isa
貫穿了我們的類對(duì)象
卦停、類
向胡、元類
、根元類
惊完。在OC
底層系統(tǒng)為我們?cè)O(shè)計(jì)了objc_object
僵芹、objc_class
,OC
上層由NSObject
作為根類持有isa
特性。isa
完美的將底層
和OC
串聯(lián)起來(lái)小槐。
結(jié)語(yǔ):萬(wàn)物皆對(duì)象
淮捆,萬(wàn)物皆有isa
。