在alloc&init&new探索中我們知道瘪校,alloc一個(gè)對(duì)象主要有cls->instanceSize
計(jì)算需要的內(nèi)存大小嗅榕、calloc(1, size)
根據(jù)所需要的內(nèi)存大小開(kāi)辟內(nèi)存空間简十、initInstanceIsa(cls, hasCxxDtor)
將isa
與內(nèi)關(guān)聯(lián)返帕;今天就來(lái)探索一下isa
是如何與類(lèi)產(chǎn)生關(guān)聯(lián)的脓鹃。
準(zhǔn)備工作:objc4-781源碼
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);
}
- 跟進(jìn)
initInstanceIsa(cls, hasCxxDtor)
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
- 這我們看出忧设,接下來(lái)會(huì)執(zhí)行
initIsa(cls, true, hasCxxDtor)
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
是在這里完成配置的崔泵。 isa
的類(lèi)型是isa_t
。那么isa
到到底是怎么配置的呢险污?
isa_t
我們跟進(jìn)isa_t
發(fā)現(xiàn)isa_t
是一個(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_t
類(lèi)型使用聯(lián)合體
是基于內(nèi)存優(yōu)化
痹愚,采用char+位域
(即二進(jìn)制中每一位均可以保存不同的信息)的原理。我們都知道isa指針
占用8
個(gè)字節(jié)蛔糯,即64
位拯腮,可以合理的將64
位利用起來(lái),極大的節(jié)約了內(nèi)存空間蚁飒。
- 聯(lián)合體
isa_t
提供了兩個(gè)成員變量cls
动壤、bits
,由聯(lián)合體定義可知cls
與bits
互斥淮逻。 - 還提供了一個(gè)
位域機(jī)構(gòu)體
琼懊,在這個(gè)位域機(jī)構(gòu)體中是一個(gè)宏ISA_BITFIELD
,根據(jù)釋義我們知道這個(gè)宏定義在isa.h
中- 位域結(jié)構(gòu)體
按二進(jìn)制位來(lái)分配內(nèi)存的結(jié)構(gòu)體爬早,冒號(hào)表示位域肩碟。位域出現(xiàn)的原因是由于某些信息的存儲(chǔ)表示只需要幾個(gè)bit位就可以表示而不需要一個(gè)完整的字節(jié),同時(shí)也是為了節(jié)省存儲(chǔ)空間和方便處理
- 位域結(jié)構(gòu)體
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
我們可以看出整個(gè)定義的聯(lián)合體位域無(wú)論是在__arm64__ (iOS)
還是__x86_64__(macOS)
中都占64
位凸椿。32
位這里就探索了削祈,一樣的原理。
- nonpointer:是否對(duì)
isa指針
開(kāi)啟指針優(yōu)化
脑漫。有兩個(gè)值髓抑,表示自定義的類(lèi)等,占1位- 0:純isa指針
- 1:不只是
類(lèi)對(duì)象地址
优幸,isa中包含了類(lèi)信息吨拍、對(duì)象的引用計(jì)算等
- has_assoc:是否有
關(guān)聯(lián)對(duì)象
- has_cxx_dtor:是否有
C++
的相關(guān)實(shí)現(xiàn)。表示該對(duì)象是否有C++/OC
的析構(gòu)器
(類(lèi)似于dealloc)网杆,占1位- 如果有
析構(gòu)函數(shù)
羹饰,則需要做析構(gòu)相關(guān)的邏輯 - 如果沒(méi)有伊滋,則可以快速
釋放
- 如果有
- shiftcls:存儲(chǔ)類(lèi)信息
-
__arm64__
中占33位 -
__x86_64__
中占44位
-
- magic:用于調(diào)試器判斷
當(dāng)前對(duì)象
是真的對(duì)象
還是沒(méi)有初始化的空間
,占6位-
__arm64__
: 0x1a -
__x86_64__
:0x3b
-
- weakly_referenced:指對(duì)象是否
被指向
或者曾經(jīng)指向一個(gè)ARC的弱變量
- 如果沒(méi)有可以更快釋放
- deallocating:標(biāo)志對(duì)象是否正在釋放內(nèi)存
- has_sidetable_rc:是否有外掛的
散列表
队秩。當(dāng)對(duì)象的引用計(jì)數(shù)大于10時(shí)笑旺,則需要借用該變量存儲(chǔ)進(jìn)位 - extra_rc:額外的引用計(jì)數(shù),實(shí)際上是引用計(jì)數(shù)值減1馍资,也就是說(shuō)如果對(duì)象引用計(jì)數(shù)為10筒主,那么extra_rc是9.如果引用計(jì)數(shù)大于10,則需要用到
has_sidetable_rc
isa
isa流程圖.png
從這張圖中我們可以看到類(lèi)的實(shí)例的
isa
指向了類(lèi)
鸟蟹,類(lèi)
的isa
指向的它的元類(lèi)
乌妙,而元類(lèi)的isa
指向了根元類(lèi)
,根元類(lèi)的isa
還是指向本身
建钥,但是根元類(lèi)NSObject
也繼承自類(lèi)NSObject
藤韵。