2016-12-05 22 views
6

我正在用Xcode操作系統測試swift結束。爲什麼Swift結束不捕獲自己?

這是我的代碼:

import UIKit 

class A{ 
    var closure:()->() = {} 

    var name: String = "A" 
    init() { 
     self.closure = { 
      self.name = self.name + " Plus" 
     } 
    } 

    deinit { 
     print(name + " is deinit") 
    } 
} 

var a: A? 
a = A() 
a = nil 

不愧是什麼,一個裝在由封閉的自我,所以是永遠不會釋放。

但是,當我在最後一行之前添加此行:

a?.closure = { a?.name = "ttt" } 

然後,我發現在輸出窗口,這意味着被釋放「A是DEINIT」。 爲什麼?是不是循環引用?

爲了測試,我使用的功能來設置閉合,其中碼是2版本:

import UIKit 

class A{ 
    var closure:()->() = {} 
    func funcToSetClosure(){ 
     self.closure = { self.name = "BBB"} 
    } 
    var name: String = "A" 
    init() { 
     self.closure = { 
      self.name = self.name + " Plus" 
     } 
    } 

    deinit { 
     print(name + " is deinit") 
    } 
} 



var a: A? 


a = A() 


a?.funcToSetClosure() 


a = nil 

同樣,也不會脫離。

所以我得到了結論,當閉包由init或類中的函數設置時,會引起循環引用,當它放在類的旁邊時,它不會引起循環引用。我對嗎?

回答

4

有保留在這兩種情況下的週期。所不同的是參考文獻的性質,而不是位置,其中設置了closure。這種差異表現在什麼需要突破週期:

  • 在「裏面」的局面,瓶蓋內引用是self。當你釋放你的參考a,即不足來打破循環,因爲循環是直接自我指涉的。要打破這個循環,在將a設置爲nil之前,您應該有設置a.closurenil,並且您沒有這樣做。

enter image description here

  • 在 「外」 的情況中,基準是a。只要您的a參考未設置爲nil,就有一個保留週期。但你最終將其設置爲nil,其中足以打破週期。

enter image description here

(插圖來自Xcode的內存圖形功能。太酷了。)

+0

感謝您的幫助,終於我明白了,也感謝Xcode的內存圖。 –

2

導致保留週期的原因是您在關閉中引用了self

var a: A? 
a = A() 
a?.closure = { a?.name = "ttt" } 
a = nil 

您將封閉更改爲不再引用self,這就是爲什麼它被釋放。

在最後一個例子中,你在閉包中再次引用self,這就是爲什麼它不釋放。有辦法解決這個問題,這篇文章是一個偉大的時間在什麼時候使用每個案例在迅速:How to Correctly handle Weak Self in Swift Blocks with Arguments

我想象你正在尋找這樣的東西,你在塊內使用一個弱引用自我。 Swift有一些新的方法來實現這一點,最常用的方法是在塊前面使用[unowned self]表示法。

init() { 
    self.closure = { [unowned self] in 
     self.name = self.name + " Plus" 
    } 
} 

更多閱讀什麼是怎麼回事:Shall we always use [unowned self] inside closure in Swift

+0

謝謝,我會全部閱讀。 –

2

隨着SIL documentation說,當你捕捉一個封閉的局部變量,它會被存儲在堆上參考計數:

捕獲局部變量和indirect值類型的有效載荷 存儲在堆上。 @box T類型是參考計數類型, 引用包含類型爲T的可變值的框。

因此,當你說:

var a : A? = A() 
a?.closure = { a?.name = "ttt" } 

有一個參考週期(你可以輕鬆驗證)。這是因爲A的實例引用了closure屬性,該屬性引用了堆分配的盒裝A?實例(由於它由閉包捕獲的事實),該實例又引用了實例A

但是,你則說:

a = nil 

哪堆分配盒裝A?實例的值設置爲.none,從而釋放其引用的A實例,因此這意味着你不再有一個參考週期,因此A可以被釋放。

只是讓a掉出的範圍,不進行指定a = nil打破基準週期,作爲A?堆上仍被的Aclosure屬性,它仍被A?保留保留實例實例。

+0

謝謝,我正在閱讀SIL文檔。 –