該文章是對(duì)前一篇 OC底層原理01-alloc流程探索 中initInstanceIsa
(給對(duì)象關(guān)聯(lián)isa)分支的一個(gè)拓展和深入
一、準(zhǔn)備工作
-
objc4
可編譯源碼,可直接跳到文章最后,下載調(diào)試好的源碼
二、源碼里面找isa
2.1 繼續(xù)obj->initInstanceIsa(cls, hasCxxDtor);
流程
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
- 我們自定義的類(lèi)都是走
initInstanceIsa
- 系統(tǒng)某一些類(lèi)才會(huì)調(diào)用
initIsa
2.2 進(jìn)入initInstanceIsa
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
inline void
objc_object::initIsa(Class cls)
{
initIsa(cls, false, false);
}
-
initInstanceIsa
也會(huì)進(jìn)入initIsa
- 它們第二個(gè)參數(shù)
nonpointer
不同 -
initInstanceIsa
是傳的true,initIsa
是傳的false
2.3 進(jìn)入initIsa
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;
}
}
- 由于我們自定義的類(lèi)
nonpointer
都是true,所以我們會(huì)進(jìn)入else
流程 - 發(fā)現(xiàn)目標(biāo)
isa
,它是一個(gè)isa_t
類(lèi)型的對(duì)象 -
isa_t newisa(0)
初始化一個(gè)isa
-
newisa.bits = hasCxxDtor
給屬性bits
賦值 -
newisa.has_cxx_dtor = hasCxxDtor
給屬性has_cxx_dtor
賦值 -
newisa.shiftcls = (uintptr_t)cls >> 3
給屬性shiftcls
賦值,并向右移三位 -
isa = newisa
把新建的newisa
賦值給對(duì)象的isa
,進(jìn)行關(guān)聯(lián)
2.4 查看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
};
-
isa_t
是一個(gè)聯(lián)合體,屬性互斥。 -
cls
和bits
不能同時(shí)賦值
2.5 查看ISA_BITFIELD
# 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
- 如果是
arm64
就走上面,如果是x86_64
就走下面 - 因?yàn)槲覀兣渲玫?code>objc4是基于
macos
的,所以我們要研究的是下面 -
isa
存在內(nèi)存地址的第一段,也就是8字節(jié),8*8=64位
2.6 斷點(diǎn)分析newisa
的賦值過(guò)程
2.6.1 newisa.bits = ISA_MAGIC_VALUE
在這一行下斷點(diǎn)
image.png
2.6.2 打印newisa
(lldb) p newisa
(isa_t) $2 = {
cls = nil
bits = 0
= {
nonpointer = 0
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 0
magic = 0
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
- 初始都為空
2.6.3 斷點(diǎn)往下走一步,再次打印newisa
(lldb) p newisa
(isa_t) $3 = {
cls = 0x001d800000000001
bits = 8303511812964353
= {
nonpointer = 1
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 0
magic = 59
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
-
cls
被賦值了0x001d800000000001
-
bits
被賦值了8303511812964353
-
nonpointer
被賦值了1
-
magic
被賦值了59
2.6.3.1 系統(tǒng)給cls
賦值的是什么
image.png
- 宏
ISA_BITFIELD
上面一個(gè)宏# define ISA_MAGIC_VALUE 0x001d800000000001ULL
,這一步可以看做把ISA_MAGIC_VALUE
直接賦值給cls
,與打印臺(tái)打印的cls
不是同一個(gè),這里的cls
是newisa
中的cls
- 這里的
ULL
是unsigned long long
無(wú)符號(hào)長(zhǎng)整型,只是一個(gè)類(lèi)型標(biāo)識(shí)
2.6.3.2 系統(tǒng)給bits
賦值的是什么
image.png
- 原來(lái)系統(tǒng)把宏
ISA_MAGIC_VALUE
的值0x001d800000000001
轉(zhuǎn)成了10進(jìn)制,賦給了newisa
中的cls
- 注意:上圖中打印的
cls
,是objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
方法傳入的,為了驗(yàn)證當(dāng)前研究的是GomuPerson
的流程
2.6.3.3 系統(tǒng)給nonpointer
賦值的是什么
- 因?yàn)?code>initIsa方法傳入的是一個(gè)true,所以賦值為1
2.6.3.4 系統(tǒng)給magic賦值的是什么
image.png
- 用
p/t
命令將0x001d800000000001
轉(zhuǎn)成2進(jìn)制打印,得上以上截圖的一串?dāng)?shù)字 - 由宏
ISA_BITFIELD
的位域定義可以得到,magic
占位是6位
,從第47位到第52位
,由于是iOS小端
,從右到左讀取,用分隔符每4位分開(kāi)方便查看0b0000_0000_0001_1101_1000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001
,得到第47位到52位為:111011
-
111011
是2進(jìn)制
,轉(zhuǎn)成10進(jìn)制
得到59
2.6.4 斷點(diǎn)繼續(xù)往下走一步,再次打印newisa
(lldb) p newisa
(isa_t) $7 = {
cls = 0x001d800000000005
bits = 8303511812964357
= {
nonpointer = 1
has_assoc = 0
has_cxx_dtor = 1
shiftcls = 0
magic = 59
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
-
has_cxx_dtor
被賦值了1
,前面?zhèn)鬟^(guò)來(lái)hasCxxDtor
為true
2.6.4 斷點(diǎn)繼續(xù)往下走一步,再次打印newisa
(lldb) p newisa
(isa_t) $8 = {
cls = GomuPerson
bits = 8303516107940741
= {
nonpointer = 1
has_assoc = 0
has_cxx_dtor = 1
shiftcls = 536872048
magic = 59
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
-
cls
的值變成了GomuPerson
,說(shuō)明isa
和GomuPerson
已關(guān)聯(lián) -
bits
的值由8303511812964357
變成了8303516107940741
-
shiftcls
被賦值了536872048
2.6.4.1 shiftcls
賦的值是什么
image.png
- 因?yàn)?code>newisa.shiftcls = (uintptr_t)cls >> 3 這一步代碼,將
cls
強(qiáng)轉(zhuǎn)成uintptr_t
(無(wú)符號(hào)長(zhǎng)整型的別名),然后右移位 - 打印
(uintptr_t)cls
得到4294976384
- 將
4294976384
右移三位,得到536872048
- 這一步的目的就是將
cls
強(qiáng)轉(zhuǎn)后右移三位的值賦給shiftcls
- 因?yàn)?code>newisa中又給
shiftcls
賦了值,所以bits
必然會(huì)變
2.6.4.2 bits
的值為什么變了
[圖片上傳中...(image.png-902d6d-1599838772142-0)]
- 把
bits
當(dāng)前的值換算成2進(jìn)制打印,得到上圖$27
- 把
shiftcls
的值換算成2進(jìn)制打印,得到上圖$28
- 把
$28
前面補(bǔ)零,補(bǔ)足44位,則與$27
完全相同 - 說(shuō)明
isa
中3-46
位的確存的是cls
,但是是右移了3位
的cls
2.6.5 反向由person
的首地址isa
推導(dǎo)出shiftcls
2.6.5.1 打印person
的isa
的地址,轉(zhuǎn)成10進(jìn)制
image.png
- 得到
8303516107940741
,很上面newisa
中的bits
完全一樣
2.6.5.2 由isa
的地址推導(dǎo)給shiftcls
賦值之前的(uintptr_t)cls
image.png
- 由于
shiftcls
在isa
內(nèi)存的3-46
位,所以我們要先抹掉前面3位,右移>>
3位 - 左邊空出20位,左移
<<
20位,再右移>>
17位,把左邊17位抹零
- 得到結(jié)果
4294976384
,和給shiftcls
賦值的(uintptr_t)cls
的值一模一樣
2.6.5.3 shiftcls
賦值的時(shí)候?qū)?code>(uintptr_t)cls右移三位的目的
- 需要將一個(gè)64位的
(uintptr_t)cls
賦值給一個(gè)從isa
第三位開(kāi)始長(zhǎng)度只有44的shiftcls
- 那么我們需要取
(uintptr_t)cls
的0-43
位賦值給shiftcls
,因?yàn)?code>shiftcls是從isa
的第三位開(kāi)始,這樣賦值的后果是shiftcls
在isa
中的值就會(huì)向左偏移移3位, - 為了保證賦值后的
shiftcls
和(uintptr_t)cls
相等,我們必須把(uintptr_t)cls
的3-46
賦值給shiftcls
,即(uintptr_t)cls>>3
2.6.6 isa
的地址 &
ISA_MASK
,會(huì)得到 GomuPerson
image.png
- 經(jīng)驗(yàn)證,
isa
的地址&
ISA_MASK
,確定會(huì)得到GomuPerson
- 把
ISA_MASK
轉(zhuǎn)成二進(jìn)制
,發(fā)現(xiàn)只有中間3-46
位有效,所以isa
與上ISA_MASK
,相當(dāng)于把前3位
和后17位
置0
2.7 基于x86_64
的位域圖
image.png
-
nonpointer
:表示是否對(duì)isa
指針開(kāi)啟指針優(yōu)化,占1位-
0
:純isa
指針 -
1
:不只是類(lèi)對(duì)象地址
,isa
中包含了類(lèi)信息
、對(duì)象的引用計(jì)數(shù)
等
-
-
has_assoc
:表示關(guān)聯(lián)對(duì)象標(biāo)志位,占1位-
0
:沒(méi)有關(guān)聯(lián)對(duì)象 -
1
:存在關(guān)聯(lián)對(duì)象
-
-
has_cxx_dtor
:表示該對(duì)象是否有C++/OC的析構(gòu)器
(dealloc
),占1位-
0
:沒(méi)有析構(gòu)函數(shù),則可以更快的釋放對(duì)象 -
1
:有析構(gòu)函數(shù),則需要做析構(gòu)邏輯
-
-
shiftclx
表示存儲(chǔ)類(lèi)的指針的值
(類(lèi)的地址), 即類(lèi)信息-
arm64
中占33
位,開(kāi)啟指針優(yōu)化
的情況下,在arm64
架構(gòu)中有33
位用來(lái)存儲(chǔ)類(lèi)指針 -
x86_64
中占44
位
-
-
magic
:用于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象
還是沒(méi)有初始化的空間
,占6位 -
weakly_refrenced
:表示對(duì)象是否被指向
或者曾經(jīng)指向一個(gè)ARC
的弱變量
,沒(méi)有弱引用的對(duì)象
可以更快釋放
,占1位 -
deallocating
:標(biāo)志對(duì)象是是否正在釋放內(nèi)存
,占1位 -
has_sidetable_rc
:表示當(dāng)對(duì)象引用計(jì)數(shù)大于10
時(shí),則需要借用該變量
存儲(chǔ)進(jìn)位
,占1位 -
extra_rc
:(額外的引用計(jì)數(shù)) --- 導(dǎo)尿管表示該對(duì)象的引用計(jì)數(shù)值
,實(shí)際上是引用計(jì)數(shù)值減1
,如果對(duì)象的引用計(jì)數(shù)為10
,那么extra_rc
為9
,占8位
四、拓展知識(shí)
構(gòu)造數(shù)據(jù)類(lèi)型的方式:
-
結(jié)構(gòu)體
[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)存
,假設(shè)有4個(gè)int成員,一共分配了16字節(jié)的內(nèi)存,但是在使用時(shí),你只使用了4字節(jié),剩余的12字節(jié)就是屬于內(nèi)存的浪費(fèi)
-
聯(lián)合體
[union
]
聯(lián)合體
也是由不同的數(shù)據(jù)
類(lèi)型組成,但其變量是互斥的
,所有的成員共占一段內(nèi)存
。而且聯(lián)合體
采用了內(nèi)存覆蓋技術(shù)
,同一時(shí)刻
只能保存一個(gè)成員的值
,如果對(duì)新的成員賦值
,就會(huì)將原來(lái)成員的值覆蓋掉
優(yōu)點(diǎn):所有成員共用一段內(nèi)存
,使內(nèi)存的使用更為精細(xì)靈活
,同時(shí)也節(jié)省了內(nèi)存空間
缺點(diǎn):包容性弱