在iOS 底層-- isa指向探究中探索了isa的指向纤掸,那么isa的結(jié)構(gòu)具體是什么樣的旬陡。從源碼中來(lái)著手研究。
一井辆、位域
在研究isa結(jié)構(gòu)的時(shí)候关筒,需要有位域的相關(guān)只是因?yàn)閕sa的機(jī)構(gòu)是一個(gè)聯(lián)合體+位域的形式
舉個(gè)例子:
坦克大戰(zhàn)的游戲 中 坦克的方向有上下左右的狀態(tài),
常見(jiàn)的寫(xiě)法:為其添加4個(gè)變量杯缺,
@interface JETank : NSObject
@property (nonatomic, assign) BOOL left;
@property (nonatomic, assign) BOOL right;
@property (nonatomic, assign) BOOL top;
@property (nonatomic, assign) BOOL bottom;
@end
這樣可以根據(jù)每個(gè)變量去拿到相應(yīng)的狀態(tài)
但是這里為其分配了多少內(nèi)存蒸播? ------- 8位 (BOOL為1 內(nèi)存對(duì)其 結(jié)果為8)
位域的方式
因?yàn)橛?|1就可以表示具體那個(gè)方向,所以我可以定義聯(lián)合體(union)并且只需要一個(gè)char的長(zhǎng)度就可以表示4個(gè)方向
@interface JETank : NSObject
{
@public
union {
uintptr_t direction;
struct {
uintptr_t left : 1;
uintptr_t right : 1;
uintptr_t top : 5; //這里定義為5 只是想說(shuō) 長(zhǎng)度可以根據(jù)不同的需求去自定義
uintptr_t bottom : 1;
};
} _jeTankDirection;
}
這樣只需要對(duì)left/right 等進(jìn)行相應(yīng)的賦值就可以滿(mǎn)足需求
具體賦值方法
JETank *tank = [JETank new];
/**
方法1:
tank->_jeTankDirection.direction = 0x81;
// 或者 tank->_jeTankDirection.direction = 0b0010 0001;
*/
/**
方法二:
*/
tank->_jeTankDirection.left = YES; // 為什么可以這樣賦值萍肆? 因?yàn)閅ES強(qiáng)轉(zhuǎn)之后為1 二進(jìn)制就是0b1 是滿(mǎn)足的
tank->_jeTankDirection.top = 31; // 這里如果賦值100會(huì)報(bào)警告(因?yàn)閠op占5位 最大值為 31)
tank->_jeTankDirection.bottom = 0b1; // 二進(jìn)制方式賦值
NSLog(@"left = %@ top = %@ right = %@ bottom = %@",@(tank->_jeTankDirection.left),
@(tank->_jeTankDirection.top),
@(tank->_jeTankDirection.right),
@(tank->_jeTankDirection.bottom));
//打印結(jié)果
left = 1 top = 31 right = 0 bottom = 1
如果top 是賦值 100 100二進(jìn)制為:0b0110 0100 因?yàn)橹徽?為 只取后5位 就是0b00100 最后打印結(jié)果為 top = 8
1袍榆、聯(lián)合體的優(yōu)勢(shì)
聯(lián)合體和結(jié)構(gòu)體寫(xiě)法上有些類(lèi)似,但是注意區(qū)分
聯(lián)合體的所有信息公用一塊內(nèi)存塘揣,起到節(jié)省內(nèi)存的作用
2包雀、位域的作用
直觀的表達(dá)取值范圍,可以直接拿到相應(yīng)的值
二亲铡、isa 結(jié)構(gòu)
我們可以從源碼中找到相關(guān)內(nèi)容:
在底層的代碼中具體為:
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
......
}
因?yàn)閭魅氲膎onpointer 為 true 所以這里的代碼可以簡(jiǎn)化為:
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
isa_t newisa(0);
newisa.bits = ISA_MAGIC_VALUE; // 對(duì)bits進(jìn)行初始化
newisa.has_cxx_dtor = hasCxxDtor; //賦值
newisa.shiftcls = (uintptr_t)cls >> 3; //賦值 與class 進(jìn)行關(guān)聯(lián)
isa = newisa;
}
在這里進(jìn)行了 isa 的初始化才写。
isa是一個(gè)聯(lián)合體
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_BITFIELD
在這里是宏定義(根據(jù)架構(gòu)不同葡兑,內(nèi)容不同),可以在進(jìn)去一層查看具體定義
__x86_64__
pc端、 __arm64__
手機(jī)端64位
這里以arm64為例:
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
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
};
};
對(duì)isa進(jìn)行初始化的時(shí)候赞草,對(duì)bits有一個(gè)默認(rèn)的賦值讹堤,ISA_MAGIC_VALUE
為宏定義,具體為
0x001d800000000001ULL
在系統(tǒng)計(jì)算機(jī)上轉(zhuǎn)換為二進(jìn)制就是64字節(jié)
圖解為:
(這張圖來(lái)源網(wǎng)絡(luò) 并稍作改動(dòng)厨疙,是針對(duì) x86_64 結(jié)構(gòu)洲守,于本文稍有出入 ( shiftcls 只到36位為止,后面依次排)
bits里面的具體信息分別代表什么意思沾凄?
bits是64為字節(jié) struct是位域
nonpointer :1 (在bits的64位字節(jié)中 第0個(gè)用于nonpointer信息存儲(chǔ))
表示是否對(duì)isa指針開(kāi)啟指針優(yōu)化岖沛;0代表純isa指針,1代表不止是類(lèi)對(duì)象指針搭独,還包含了類(lèi)信息婴削、對(duì)象的引用計(jì)數(shù)等;
has_assoc:1 (在bits的64位字節(jié)中 第1個(gè)用于has_assoc信息存儲(chǔ))
關(guān)聯(lián)對(duì)象標(biāo)志位牙肝,0沒(méi)有唉俗,1存在
has_cxx_dtor:1 (在bits的64位字節(jié)中 第2個(gè)用于has_cxx_dtor信息存儲(chǔ))
該對(duì)象是否有C++或者Objc的析構(gòu)器,如果有析構(gòu)函數(shù)配椭,則需要做析構(gòu)邏輯虫溜,如果沒(méi)有,則可以更快的釋放對(duì)象股缸;
shiftcls:33 (在bits的64位字節(jié)中 第3-35用于shiftcls信息存儲(chǔ))
存儲(chǔ)類(lèi)指針的值衡楞。開(kāi)啟指針優(yōu)化的情況下,在arm64架構(gòu)中有33位用來(lái)存儲(chǔ)類(lèi)指針敦姻;
magic:6 (在bits的64位字節(jié)中 第36-41用于magic信息存儲(chǔ))
用于調(diào)試器判斷當(dāng)前對(duì)象是真的對(duì)象還是沒(méi)有初始化的空間瘾境;
weakly_referenced :1 (在bits的64位字節(jié)中 第42個(gè)用于weakly_referenced信息存儲(chǔ))
標(biāo)志對(duì)象是否被指向或者曾經(jīng)指向一個(gè)ARC的弱變量,沒(méi)有弱引用的對(duì)象可以更快釋放镰惦;
deallocating :1
標(biāo)志對(duì)象是否正在釋放內(nèi)存迷守;
has_sidetable_rc :1
當(dāng)對(duì)象引用計(jì)數(shù)大于10時(shí),則需要借用該變量存儲(chǔ)進(jìn)位
extra_rc :19
當(dāng)表示該對(duì)象的引用計(jì)數(shù)值旺入,實(shí)際上是引用計(jì)數(shù)值減1兑凿,例如,如果對(duì)象的引用計(jì)數(shù)為10茵瘾,那么extra_rc為9.如果引用計(jì)數(shù)大于10礼华,則需要使用上面提到的has_sidetable_rc。
三拗秘、結(jié)構(gòu)體(struct)與聯(lián)合體(union)
union
1圣絮、可以定義多個(gè)成員,大小由最大的成員的大小決定聘殖。
2晨雳、成員共享同一塊大小的內(nèi)存,一次只能使用其中的一個(gè)成員奸腺。
3餐禁、對(duì)某一個(gè)成員賦值,會(huì)覆蓋其他成員的值(也不奇怪突照,因?yàn)樗麄児蚕硪粔K內(nèi)存帮非。但前提是成員所占字節(jié)數(shù)相同,當(dāng)成員所占字節(jié)數(shù)不同時(shí)只會(huì)覆蓋相應(yīng)字節(jié)上的值讹蘑,比如對(duì)char成員賦值就不會(huì)把整個(gè)int成員覆蓋掉末盔,因?yàn)閏har只占一個(gè)字節(jié),而int占四個(gè)字節(jié))
聯(lián)合體
4座慰、的存放順序是所有成員都從低地址開(kāi)始存放的陨舱。
- 簡(jiǎn)而言之: union的特點(diǎn):共用一塊內(nèi)存,大小由最長(zhǎng)的那個(gè)成員決定
對(duì)某一個(gè)成員賦值版仔,會(huì)影響其他成員的值
結(jié)構(gòu)體
本質(zhì)上是多個(gè)變量集合到一起游盲,多個(gè)變量是同時(shí)存在的,互不影響÷福總體的大小是各個(gè)變量值所在內(nèi)存大小的和(由于內(nèi)存對(duì)齊的原則益缎,總體大小總是>=這個(gè)和值)。