HeapObject
在Swift中,一個(gè)Class對象實(shí)際上就是一個(gè)HeapObject結(jié)構(gòu)體指針放钦。那么它的內(nèi)存布局是怎樣的呢? 首先我們先來看一下 HeapObject 的結(jié)構(gòu):
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
HeapObject() = default;
// Initialize a HeapObject header as appropriate for a newly-allocated object.
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized)
{ }
// Initialize a HeapObject header for an immortal object
constexpr HeapObject(HeapMetadata const *newMetadata,
InlineRefCounts::Immortal_t immortal)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Immortal)
{ }
};
從中我們可以看到:
第一個(gè)字段是一個(gè) Metadata 對象操禀,該對象有著與OC中 isa_t 類似的作用颓屑,用來描述對象類型的(等價(jià)于 type(of:) 取得的結(jié)果)! 但本文的重點(diǎn)不是在這耿焊,我們繼續(xù)往下看!
這才是本文的主角哦: SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS (這是一個(gè)宏定義) !
我們點(diǎn)開這個(gè)宏來看看這是個(gè)啥?
RefCounts<InlineRefCountBits> refCounts;
這就是Swift中引用計(jì)數(shù) refCounts了,引用計(jì)數(shù)器腋、弱引用纫塌、unowned 引用都與它有關(guān)!
繼續(xù)深挖定義refCounts的這個(gè)類 InlineRefCounts
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
typedef RefCounts<SideTableRefCountBits> SideTableRefCounts
InlineRefCounts: 是用在常規(guī)對象以及無主引用中
SideTableRefCounts: 則是用在weak引用對象中
InlineRefCounts 被起了個(gè)別名為 RefCounts讲弄,我們看繼續(xù)看下 RefCounts類結(jié)構(gòu)
template <typename RefCountBits>
class RefCounts {
std::atomic<RefCountBits> refCounts;
....
}
這里省略了所有的方法和類型定義! 注意: 這是模版類避除,那么就要注意傳進(jìn)來的模版參數(shù)
首先我們來看看 InlineRefCountBits (常規(guī)對象)
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
template <RefCountInlinedness refcountIsInline>
class RefCountBitsT {
friend class RefCountBitsT<RefCountIsInline>;
friend class RefCountBitsT<RefCountNotInline>;
static const RefCountInlinedness Inlinedness = refcountIsInline;
typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
BitsType;
typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::SignedType
SignedBitsType;
typedef RefCountBitOffsets<sizeof(BitsType)>
Offsets;
BitsType bits;
// ...
};
通過模板替換之后瓶摆,InlineRefCountBits 實(shí)際上就是一個(gè) uint64_t! 位圖如下:
接下來我們在看看 SideTableRefCounts (weak引用)
首先我們先來寫一段代碼群井,看看能從匯編中發(fā)現(xiàn)什么?
class People {
var age = 10
}
let p = People()
weak var p2 = p
咦,這個(gè)swift_weakInit是什么? 它又做了什么? 我們繼續(xù)往里看
WeakReference *swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
ref->nativeInit(value);
return ref;
}
從上述代碼可以看出該對象為WeakReference類! 那么 weak 變量指蚁, 編譯器則會在聲明的過程中就定義了WeakReference對象自晰,該對象主要目的就是用來管理當(dāng)前的弱引用對象!
該對象調(diào)用了其中的 nativeInit(value) 方法酬荞;傳遞了當(dāng)前的HeapObject對象
繼續(xù)看 nativeInit 具體操作
void nativeInit(HeapObject *object) {
auto side = object ? object->refCounts.formWeakReference() : nullptr;
nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
}
如果當(dāng)前對象不為空則調(diào)用object->refCounts.formWeakReference()方法、我們查看下 formWeakReference方法的實(shí)現(xiàn)
template <> HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::formWeakReference()
{
auto side = allocateSideTable(true);
if (side)
return side->incrementWeak();
else
return nullptr;
}
創(chuàng)建SideTable枪向、如果創(chuàng)建成功秘蛔,則執(zhí)行 incrementWeak();
查看allocateSideTable創(chuàng)建過程
template <>
HeapObjectSideTableEntry* RefCounts<InlineRefCountBits>::allocateSideTable(bool failIfDeiniting)
{
auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);
// Preflight failures before allocating a new side table.
if (oldbits.hasSideTable()) {
// Already have a side table. Return it.
return oldbits.getSideTable();
}
else if (failIfDeiniting && oldbits.getIsDeiniting()) {
// Already past the start of deinit. Do nothing.
return nullptr;
}
HeapObjectSideTableEntry *side = new HeapObjectSideTableEntry(getHeapObject());
auto newbits = InlineRefCountBits(side);
....
return side;
}
- 首先拿到原有的引用計(jì)數(shù) refCounts.load(SWIFT_MEMORY_ORDER_CONSUME)
- 然后通過 getHeapObject() 創(chuàng)建一個(gè) HeapObjectSideTableEntry 實(shí)例對象 side
- 再把創(chuàng)建出來的 side對象地址給 InlineRefCountBits 深员、而InlineRefCountBits 就是我們分析強(qiáng)引用計(jì)數(shù)的 RefCountBitsT!
順便一起來看一下 HeapObjectSideTableEntry 的結(jié)構(gòu):
class HeapObjectSideTableEntry {
std::atomic<HeapObject*> object;
SideTableRefCounts refCounts;
public:
HeapObjectSideTableEntry(HeapObject *newObject)
: object(newObject), refCounts()
{ }
......
}
在HeapObjectSideTableEntry中保留了原對象的Metadata和refCound!
還有bits(強(qiáng)引用蛙埂、無主引用的信息) 以及新增的 weakBits(弱引用信息)
小結(jié)
swift對象都是以HeapObject為模板創(chuàng)建!
RefCounts:
第一種是 InlineRefCounts 绣的,用在 常規(guī)對象(無主) 中,它其實(shí)是一個(gè) uint64_t!
第二種是SideTableRefCounts, 用在weak對象中芭概,此時(shí)它是一個(gè)指向 SideTable(HeapObjectSideTableEntry) 的指針盼理。
注意點(diǎn): OC與Swift區(qū)別
OC:
弱引用計(jì)數(shù)是存放在全局維護(hù)的散列表中俄删,isa中會記錄是否使用了散列表。
在引用計(jì)數(shù)為0時(shí)臊诊,自動觸發(fā)dealloc斜脂,會檢查并清空當(dāng)前對象的散列表計(jì)數(shù)。
Swift
弱引用計(jì)數(shù)也是存放在散列表中玷或,這個(gè)散列表是局部的! 每個(gè)weak對象都有自己的散列表!
即便一個(gè)對象在使用了弱引用后南窗,也不能保證相關(guān)內(nèi)存全部被釋放! 因?yàn)镾ideTable 的生命周期與對象是分離的奠骄,當(dāng)強(qiáng)引用計(jì)數(shù)為 0 時(shí)部服,只有 HeapObject 被釋放了,但不會觸發(fā)SideTable的清空鹅髓。而是在下次訪問時(shí)發(fā)現(xiàn)當(dāng)前對象為nil時(shí),才會清空SideTable