之前我在CSDN上寫過一篇博客:OC內(nèi)存管理踱蠢、ARC火欧、property屬性、__strong、__weak布隔、__blockhttp://blog.csdn.net/u012297622/article/details/50698286离陶,大家有興趣的可以去看看稼虎。
今天我們來整理一下Swift的內(nèi)存管理與循環(huán)引用的解決問題-weak衅檀、unowned:
內(nèi)存管理
swift的內(nèi)存管理也是使用的ARC(自動引用技術(shù)):當(dāng)我們初始化創(chuàng)建一個對象實例的時候,swift就會替我們管理和分配內(nèi)存,此時的引用計數(shù)為1,當(dāng)對其進(jìn)行init(copy/mutableCopy)時,引用計數(shù)會+1霎俩,而當(dāng)實例被銷毀時哀军,引用計數(shù)就會-1。當(dāng)系統(tǒng)檢測到引用計數(shù)為0的時候打却,就會釋放掉這個內(nèi)存杉适。
但是,這種引用計數(shù)會產(chǎn)生一個問題就是循環(huán)引用:
循環(huán)引用
class A {
? ? ? ?var b:B?
? ? ? init() { print("A初始化") }
? ? ?deinit { print("A析構(gòu)掉") }
}
class B {
? ? ? ?var a:A?
? ? ? ?init() { print("B初始化") }
? ? ? ?deinit { print("B析構(gòu)掉") }
}
var a:A?; ? a = A()
var b:B?; ? b = B()
a!.b = b; ? b!.a = a
a = nil;? ? b = nil
你會發(fā)現(xiàn)柳击,A和B的析構(gòu)函數(shù)deinit都沒有調(diào)用猿推,因為當(dāng)a執(zhí)行析構(gòu)的時候,b.a還在對其進(jìn)行引用捌肴,當(dāng)b析構(gòu)的時候蹬叭,a.b也在對b進(jìn)行引用。這時候解決的方法就是對其中的某一個聲明進(jìn)行若引用状知,即加上weak:
weak var b:B?
另外一種造成循環(huán)引用的問題就是閉包:閉包中對任何元素的引用都會被閉包自動持有秽五,如果我們在閉包中需要使用self的話,那就相當(dāng)于閉包對self持有饥悴,而block又是被self直接或間接持有坦喘,這樣就造成了循環(huán)引用。例如下面的代碼:
class C{
? ? ? ?var name:String
? ? ? ?lazy var block:()->() = {
? ? ? ?print(self.name )
}
init(name:String) {
? ? ? ?self.name = name
? ? ? ?print("C初始化")
}
deinit {
print("C析構(gòu)")
}
}
var c:C? = C(name:"c")
c?.block()
c = nil
這里C的析構(gòu)函數(shù)也是沒有執(zhí)行的西设。block是self的屬性瓣铣,block里面又對self持有,這就形成了循環(huán)引用贷揽。所以這里我們可以使用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)
}
}
那么這兩個使用有什么區(qū)別呢?接下來看一個例子:
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析構(gòu)")
}
}
class D{
var block:(()->())!
init(callBack:(()->())?) {
self.block = callBack!
print("D構(gòu)造")
}
deinit {
print("D析構(gòu)")
}
}
var c:C? = C(name:"c")
var d = D.init(callBack:c?.block)
c!.block()
c = nil
d.block()
這里當(dāng)你運行到 d.block()的時候擒滑,是會有一個error的
因為當(dāng)d.block()執(zhí)行的時候腐晾,c已經(jīng)被析構(gòu)掉了,而閉包里的self肯定也是不存在的丐一,是一個nil藻糖,這個時候執(zhí)行的話self.name就會報錯。所以在我們不確定是否有外部變量在持有這個block的時候库车,我們就應(yīng)該使用weak更為安全巨柒,因為使用weak的話self.name需要改成可選性的self?.name,這個時候self?.name肯定就為nil了。所以換成weak之后洋满,在playground里的d.block()就不會有錯誤了晶乔,而且block也是會正常執(zhí)行的,只不過print(self?.name)打印出來為nil牺勾。
歡迎大家訪問我的github:https://github.com/FCF5646448正罢,如果有能幫助到大家的地方,可以鼓勵我個小星星哦