之前我在CSDN上寫過一篇部落格:OC記憶體管理、ARC、property屬性、__strong、__weak(),大家有興趣的可以去看看。
今天我們來整理一下Swift的記憶體管理與循環引用的解決問題-weak、unowned:
記憶體管理
swift的記憶體管理也是使用的ARC(自動引用技術):當我們初始化建立一個對象執行個體的時候,swift就會替我們管理和配置設定記憶體,此時的引用計數為1,當對其進行init(copy/mutableCopy)時,引用計數會+1,而當執行個體被銷毀時,引用計數就會-1。當系統檢測到引用計數為0的時候,就會釋放掉這個記憶體。
但是,這種引用計數會産生一個問題就是循環引用:
循環引用
class A {
var b:B?
init() { print("A初始化") }
deinit { print("A析構掉") }
}
class B {
var a:A?
init() { print("B初始化") }
deinit { print("B析構掉") }
}
var a:A?; a = A()
var b:B?; b = B()
a!.b = b; b!.a = a
a = nil; b = nil
你會發現,A和B的析構函數deinit都沒有調用,因為當a執行析構的時候,b.a還在對其進行引用,當b析構的時候,a.b也在對b進行引用。這時候解決的方法就是對其中的某一個聲明進行若引用,即加上weak:
weak var b:B?
另外一種造成循環引用的問題就是閉包:閉包中對任何元素的引用都會被閉包自動持有,如果我們在閉包中需要使用self的話,那就相當于閉包對self持有,而block又是被self直接或間接持有,這樣就造成了循環引用。例如下面的代碼:
class C{
var name:String
lazy var block:()->() = {
print(self.name )
}
init(name:String) {
self.name = name
print("C初始化")
}
deinit {
print("C析構")
}
}
var c:C? = C(name:"c")
c?.block()
c = nil
這裡C的析構函數也是沒有執行的。block是self的屬性,block裡面又對self持有,這就形成了循環引用。是以這裡我們可以使用unowned,也可以使用weak:
//unowned
lazy var block:()->() = {[unowned self] in
print(self.name)
}
//weak
lazy var block:()->() = {[weak self] in
if let strongSelf = self{
print(strongSelf.name)
}
}
那麼這兩個使用有什麼差別呢?接下來看一個例子:
class C{
var name:String
lazy var block:()->() = {[unowned self] in
print(self.name)
}
init(name:String) {
self.name = name
print("C初始化")
}
deinit {
print("C析構")
}
}
class D{
var block:(()->())!
init(callBack:(()->())?) {
self.block = callBack!
print("D構造")
}
deinit {
print("D析構")
}
}
var c:C? = C(name:"c")
var d = D.init(callBack:c?.block)
c!.block()
c = nil
d.block()
這裡當你運作到 d.block()的時候,是會有一個error。
因為當d.block()執行的時候,c已經被析構掉了,而閉包裡的self肯定也是不存在的,是一個nil,這個時候執行的話self.name就會報錯。是以在我們不确定是否有外部變量在持有這個block的時候,我們就應該使用weak更為安全,因為使用weak的話self.name需要改成可選性的self?.name,這個時候self?.name肯定就為nil了。是以換成weak之後,在playground裡的d.block()就不會有錯誤了,而且block也是會正常執行的,隻不過print(self?.name)列印出來為nil。
歡迎大家通路我的github:https://github.com/FCF5646448,如果有能幫助到大家的地方,可以鼓勵我個小星星哦