iT邦幫忙

2021 iThome 鐵人賽

DAY 16
0

當一個類別實體被指派值(給一個屬性、常數或變數)的時候,會建立一個該實體的強參考(strong reference),同時會將參考計數(reference counting)加 1 ,強參考表示會將這個實體保留住,只要強參考還在(也就是參考計數不為 0 ),儲存這個實體的記憶體就不會被釋放掉。

這是什麼意思呢?

簡言之,就是當你舉了一實體的例子,你就會建立了一個這個實體的強參考,也會在自動計數裡加1,而強參考就會在你舉的這個實體裡存在,而只要強參考的內容還在,這個記憶體就不會不見。

就以上例來說,ARC的作用就是在你將一個變數明顯的指向某一個“有值”的內容的時候,ARC就會自動加1。

而如若指向nil,變數的狀態就會讓參考計數變低,多一個nil值,也就少一個參考計數。

類別實體間的強參考循環

ARC 在大部分時間都可以運作順利,但在有些情況下會造成強參考永遠不會歸零,進而發生記憶體洩漏(memory leak)的問題。

以下的例子是兩個類別彼此都擁有對方強參考的屬性,一個實體要釋放記憶體前,必須先釋放對方強參考,而對方要釋放前也是要原本實體先釋放,進而產生強參考循環。

就以上述例子來說,其實最重要的是從18行到21行的位置,18–19行我們把變數分別指向了對方,然後在21–22行的位置,我們把他們兩個都指向了空值,但是這個時候ARC在運作的時候,因為各有一方指向對方的強參考,就算我們表面上看不出來,強參考的值就會是2,而沒辦法解開。
也因為強參考循環的情形是可能會造成在編寫程式的時候,產生困擾,因此,Swift為了解決強參考循環的問題,因此有兩個解決方案:

  1. 弱參考(weak reference)
  2. 無主參考(unowned reference)

這兩種參考也能參考實體,但因為不是強參考,所以不會保留住實體的參考(也就是這個實體的參考計數不會增加)。
而兩者的差別在於,如果一個參考這個實體的變數在生命週期中,可能會為nil時,就使用弱參考,而在初始化之後不會再變為nil的則是使用無主參考。

為求理解,我們以實例來探討:

弱參考

在上述此例中,我們應該先抓到關鍵字:weak var tenant:Person?也就是說,現在tenant這個變數現在是一個弱參考。

那再往下看,我們可以知道,joe2承載著類別Person的內容,而oneUnit2承載著類別Apartment的內容,而oneUnit2裡面有一個弱參考的變數。

而接著是Joe2強力解包,將住屋情況指為oneUnit2,但oneUnit2是弱參考,所以不會增加joe2的參考計數,
oneUnit2強力解包,將內容指為joe2。這個時候joe2內含弱參考的變數,尚且不會增加計數。

接著,將兩者都分別指向nil,斷開強參考,於是,joe2的實體參考會減為0,而實體的弱參考則為nil,joe2被釋放後,oneUnit2的實體減為1,接著oneUnit2斷開強參考指派為nil,參考計數為0。

綜合實例所及,弱參考的關鍵字帶入後,即會為強參考的循環的問題找到出路,而原理則是被註為弱參考的變數不被計數所計,所以可以將讓另一個受制為強參考的變數變為0

無主參考

與弱參考一樣,無主參考(unowned reference)不會保留住參考的實體(所以這個實體的參考計數不會增加),但不同的是,無主參考會被視為永遠有值,所以需要被定義為非可選型別,而因此可以直接存取,不需要強制解析(即加上驚嘆號!)。

總之,在這裡要注意的是無主參考被設定後,如第10行,且尚無需註明型別,實際作用的時候,則在第17行,jess!.card強力解包後,裡面的參數customer給定jess,註明驚嘆號,這個時候非可選型別的註記讓jess不管怎麼樣都可以使用。

這個時候,具有實體的是jess,它具有一個實體,因為我們給定了name:”jess”,參考計數為1

而jess.card也有計數,我們給定number:12345678,所以它也有一個計數,但CreditCard的計數為無主參考,所以沒有計數。

總之,jess如果等於nil值,也就意味著CreditCard的強參考斷開,因而釋放。

稍微回顧一下這篇在描述的內容:
當一個類別實體被指派值(給一個屬性、常數或變數)的時候,會建立一個該實體的強參考(strong reference),同時會將參考計數(reference counting)加 1。

也就是說,只要在類別Class裡面,有一個指派值的時候,無論是常數、變數,都會建立一個強參考,同時會讓參考計數+1。
所以,也就是要留意,屬性、常數、變數在類別裡面的成立與否。

就此,我們在這篇的內容裡導引出一個結論,就是正常情況下,我們是可以將參考計數歸零的(也就是全部都斷開強參考,讓實體指向nil)。

But!偏偏就是有一種情況:叫做強參考循環,它會讓兩個屬性都是可選類型的情況,在賦值之後,各有參考計數,但因為需要,而讓對方的屬性內容成為對方的實體,這種情形,最後要將兩個參考計數指為nil的時候,會沒辨法讓參考計數歸零。
於是,便延伸出兩種排除強參考循環的方式,叫做弱參考、無主參考。

弱參考的作法是讓其中一個屬性被賦予弱參考的性質,讓它變得可以不被參考計數,於是兩實體就算互指對方的屬性,弱參考也可以因為它的特性,使被置名為弱參考的屬性不計參考計數,而跳脫強參考循環,讓參考計數能歸零,記憶體被釋放。

而無主參考的作法則是不會保留參考的實體,但卻會永遠有值,總之,它在實際上是不會被計算到參考計算的,所以被宣布為無主參考的屬性,嚴格來說不算是一個“有值”的屬性,但它對於Swift來說,就是一個“有值”的狀態,所以,也因為這樣,它得以繞過ARC針對類別裡面的屬性、變數、常數進行的計算。

如此,無主參考的原理就是利用它本身“永遠有值“,卻“不會保留實體”的特性,來繞過ARC的運作規則。

所以,無主參考的使用下,強參考循環也因此不受制於兩邊的屬性都互相指名對方是對方的屬性,而無法變為nil,釋放記憶體。

tags: 鐵人賽

上一篇
# Day15--今天,我想來點.......擴展
下一篇
# Day17--那天....我學Wendy跪著讀完的OOP
系列文
Swift30天:從語法到觀念,告訴你在踏入實作前最好弄清楚的那些事30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言