該篇主要是關(guān)于Swift的內(nèi)存管理規(guī)則壁肋。
1. 引用計(jì)數(shù)
在Class的底層結(jié)構(gòu)分析中返奉,我們可以知道HeapObject由兩部分組成动分。
struct HeapObject {
HeapMetadata const *metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
...
}
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
不同于OC將引用計(jì)數(shù)保存在引用計(jì)數(shù)表(散列表触幼,鍵為內(nèi)存塊地址,值為引用計(jì)數(shù))的記錄中恨闪。Swift的引用計(jì)數(shù)是存儲(chǔ)在HeapObject倘感,也就是實(shí)例對(duì)象中放坏。
這個(gè)refCounts本質(zhì)就是一個(gè)64位的信息咙咽。(中間忽略N多源碼)
關(guān)于引用計(jì)數(shù)的規(guī)則,主要分成以下兩種情況淤年,如圖我們也可以看出來(lái)區(qū)別钧敞。
無(wú)弱引用的情況
無(wú)弱引用的情況,引用計(jì)數(shù)的數(shù)據(jù)結(jié)構(gòu)類似這樣的(一人占32位麸粮,但是其中有些位是用來(lái)做其他判斷):
struct InlineRefCountBits {
var strongref: UInt32
var unownedRef: UInt32
}
在調(diào)試實(shí)例對(duì)象的內(nèi)存結(jié)果中溉苛,我們知道0x0000000600000002就是我們的InlineRefCountBits(引用計(jì)數(shù)信息)。從最開始的引用計(jì)數(shù)結(jié)構(gòu)圖中弄诲,我們可以看出33 - 62位存儲(chǔ)的是強(qiáng)引用數(shù)量愚战,二進(jìn)制的 11娇唯, 表示的是3,也就是有3個(gè)強(qiáng)引用計(jì)數(shù)寂玲。強(qiáng)引用計(jì)數(shù)是從0開始的塔插,有t, t1, t2 三個(gè)變量指向創(chuàng)建的對(duì)象,所以對(duì)象的強(qiáng)引用計(jì)數(shù)為3拓哟。
那控制臺(tái)里打印的0x0000000600000002中的6是怎么回事呢?
由于我們?cè)诳刂婆_(tái)打印的是16進(jìn)制想许,每4位為一組,第32 - 36位的 0110断序, 表示的是數(shù)字6流纹,所以才會(huì)顯示成0x0000000600000002,這里只是進(jìn)制計(jì)算方式不同,并不是代表有6個(gè)引用計(jì)數(shù)违诗。
由于二進(jìn)制是從第33位開始存儲(chǔ)的值是11漱凝,16進(jìn)制卻從32位開始存儲(chǔ)的值是110。110 比 11向左移1位较雕,所以就形成了2倍關(guān)系碉哑。所以我們從控制臺(tái)打印出來(lái)的值,除以2亮蒋,就是真實(shí)的強(qiáng)引用計(jì)數(shù)了扣典。
所以說,強(qiáng)引用計(jì)數(shù)和無(wú)主引用計(jì)數(shù)是通過位移的方式慎玖,存儲(chǔ)在這64位的信息當(dāng)中贮尖。簡(jiǎn)單可以理解為,這64位信息主要由強(qiáng)引用計(jì)數(shù)和無(wú)主引用計(jì)數(shù)組成趁怔。
有弱引用的情況
有弱引用的情況湿硝,64位信息不夠用了,那就需要?jiǎng)?chuàng)建新的對(duì)象來(lái)存儲(chǔ)润努。
在弱引用的創(chuàng)建過程关斜,會(huì)調(diào)用swift_weakInit,這個(gè)函數(shù)是由WeakReference來(lái)調(diào)用的铺浇,相當(dāng)于weak字段在編譯器聲明過程中就自定義了一個(gè)WeakReference的對(duì)象痢畜,其目的在于管理弱引用。
struct WeakReference {
var entry: HeapObjectSideTableEntry
}
struct HeapObjectSideTableEntry {
var object: HeapObject
var refCounts: SideTableRefCounts
}
struct SideTableRefCounts {
var strongref: UInt32
var unownedRef: UInt32
var weakBits: UInt32
}
當(dāng)使用弱引用的時(shí)候鳍侣,我們會(huì)查看當(dāng)前對(duì)象的SideTable是否已經(jīng)創(chuàng)建了丁稀,如果創(chuàng)建了,SideTable中弱引用計(jì)數(shù)加一倚聚,如果沒有創(chuàng)建线衫,那么先創(chuàng)建,把當(dāng)前對(duì)象的引用計(jì)數(shù)存在SideTable中惑折,在把弱引用計(jì)數(shù)加一授账。操作完后枯跑,我們把SideTable處理過的地址賦給當(dāng)前對(duì)象的引用計(jì)數(shù)。
換句話說白热,一旦我們使用了weak修復(fù)詞全肮,那么對(duì)象引用計(jì)數(shù)的內(nèi)存里存放的不在是強(qiáng)引用和無(wú)主引用的個(gè)數(shù),而是對(duì)應(yīng)SideTable的地址棘捣,真正的強(qiáng)引用和無(wú)主引用的個(gè)數(shù)存在了SideTable中辜腺。
2. 總結(jié)
初始化(init):在第1、2位的bit上置為1乍恐,相當(dāng)于初始化完0x3评疗。(對(duì)象初始化時(shí),引用計(jì)數(shù)傳入的默認(rèn)參數(shù)是強(qiáng)引用0茵烈,無(wú)主引用1百匆。)
無(wú)主引用(unowned):每次使用,在第2位的bit位上加1呜投,相當(dāng)于每次加0x2加匈。
強(qiáng)引用(strong):每次使用,在第33位的bit位上加1仑荐,相當(dāng)于每次加0x200000000雕拼。
弱引用(weak):每次使用,會(huì)生成一張SideTable粘招,然后把SideTable的地址右移3位啥寇,將63、64位的bit置為1洒扎,最后存入引用計(jì)數(shù)辑甜,因?yàn)樽罡呶坏膬蓚€(gè)都是1,所以顯示成16進(jìn)制的時(shí)候袍冷,最高位大概率位c磷醋。每次使用,在第1位的bit位上加1胡诗,相當(dāng)于每次加0x1邓线。
舉個(gè)例子??:
class Teacher {}
var person = Teacher()
unowned var person1 = person
unowned var person2 = person
unowned var person3 = person
var person4 = person
var person5 = person
var person6 = person
weak var person7 = person
weak var person8 = person
weak var person9 = person
無(wú)弱引用的調(diào)試情況如下:
有弱引用的調(diào)試情況如下:
所以說,對(duì)于HeapObject來(lái)說乃戈,其refCounts有兩種:
無(wú)弱引用:strongCount + unownedCount
有弱引用:object + xxx + (strongCount + unownedCount) + weakCount