實(shí)例講解Swift中引用類型的ARC自動引用計數(shù)
一、引言
ARC(自動引用計數(shù))是Objective-C和Swift中用于解決內(nèi)存管理問題的方案。在學(xué)習(xí)Objective-C編程時經(jīng)常會學(xué)習(xí)到一個關(guān)于ARC的例子:在一個公用的圖書館中,每次進(jìn)入一人就將卡插入,走的時候?qū)⒆约旱目ò纬瞿米?。圖書館系統(tǒng)會判定只要有卡插入,就將圖書館的燈打開,當(dāng)所有卡都被取走后,將圖書館的燈關(guān)掉。這個例子對應(yīng)于Objective-C中的對象聲明周期管理十分貼切。每當(dāng)一個對象增加一個引用時,其引用計數(shù)會加1,當(dāng)一個引用被取消時,對象的引用計數(shù)減1,當(dāng)引用計數(shù)減為0時,說明此對象將不再有任何引用,對象會被釋放掉,讓出內(nèi)存。Swift也采用同樣的方式進(jìn)行內(nèi)存管理。
注意:在Swift中只有引用類型有自動引用計數(shù),結(jié)構(gòu)體、枚舉這類值類型是沒有引用計數(shù)的。關(guān)于引用計數(shù)的示例代碼如下:
class MyClass {
deinit{
print("MyClass deinit")
}
}
var cls1:MyClass? = MyClass()
var cls2:MyClass? = cls1
var cls3:MyClass? = cls2
cls2 = nil
cls1 = nil
//執(zhí)行下面代碼后才會打印“MyClass deinit”
cls3 = nil
二、循環(huán)引用的處理方法
在開發(fā)中,開發(fā)者一不小心就會寫出產(chǎn)生循環(huán)引用的代碼,在上面的示例中可以看出,除非實(shí)例的引用全部解除,否則實(shí)例將不會調(diào)用析構(gòu)方法,內(nèi)存不會被釋放,如果在寫代碼時,A引用了B,同樣B也引用了A,那么實(shí)際上現(xiàn)在A和B的引用計數(shù)都是2,將A和B都置為nil后,A和B實(shí)例依然保有1個引用計數(shù),都不會被釋放,實(shí)例如下:
class MyClassOne {
var cls:MyClassTwo?
deinit{
print("ClassOne deinit")
}
}
class MyClassTwo {
var cls:MyClassOne?
deinit{
print("ClassTwo deinit")
}
}
var obj1:MyClassOne? = MyClassOne()
var obj2:MyClassTwo? = MyClassTwo()
obj1?.cls = obj2
obj2?.cls = obj1
obj1=nil
obj2=nil
//沒有打印析構(gòu)函數(shù)的調(diào)用信息
對于上面的情況,可以將屬性聲明稱weak類型來防止這種循環(huán)引用,weak的作用在于只是弱引用實(shí)例,原實(shí)例的引用計數(shù)并不會加1,示例如下:
//關(guān)于弱引用的演示
class MyClassThree{
weak var cls:MyClassFour?
deinit{
print("ClassThree deinit")
}
}
class MyClassFour {
var cls:MyClassThree?
deinit{
print("ClassFour deinit")
}
}
var obj3:MyClassThree? = MyClassThree()
var obj4:MyClassFour? = MyClassFour()
obj3?.cls = obj4
obj4?.cls = obj3
obj4=nil
//此時obj3中的cls也為nil
obj3?.cls
若引用的實(shí)例被釋放后,其在另一個實(shí)例中的引用也將被置為nil,所以weak只能用于optional類型的屬性,然而在開發(fā)中還有一種情況,某個類必須保有另一個類的示例,這個實(shí)例不能為nil,但是這個屬性又不能影響其原始實(shí)例的釋放,這種情況也會造成循環(huán)引用,示例如下:
class MyClassFive{
var cls:MyClassSix
init(param:MyClassSix){
cls = param
}
deinit{
print("ClassFive deinit")
}
}
class MyClassSix{
var cls:MyClassFive?
deinit{
print("ClassSix deinit")
}
}
var obj6:MyClassSix? = MyClassSix()
var obj5:MyClassFive? = MyClassFive(param: obj6!)
obj6?.cls = obj5
obj5=nil
obj6=nil
//沒有打印任何信息
上面的示例也會造成循環(huán)引用,然而MyClassFive類中的cls屬性為常量不可為nil,不可使用weak弱引用來做Swift中又提供了一個關(guān)鍵字unowned無主引用來處理這樣的問題,示例如下:
class MyClassFive{
unowned var cls:MyClassSix
init(param:MyClassSix){
cls = param
}
deinit{
print("ClassFive deinit")
}
}
class MyClassSix{
var cls:MyClassFive?
deinit{
print("ClassSix deinit")
}
}
var obj6:MyClassSix? = MyClassSix()
var obj5:MyClassFive? = MyClassFive(param: obj6!)
obj6?.cls = obj5
obj5=nil
obj6=nil
關(guān)于弱引用和無主引用,其區(qū)別主要是在于:
1.弱引用用于解決Optional值的引起的循環(huán)引用。
2.無主引用用于解決非Optional值引起的循環(huán)引用。
3.個人以為,弱引用可用下圖表示:

4.無主引用可用如下圖表示:

若將上面的代碼修改如下,程序會直接崩潰:
class MyClassFive{
unowned var cls:MyClassSix
init(param:MyClassSix){
cls = param
}
deinit{
print("ClassFive deinit")
}
}
class MyClassSix{
var cls:MyClassFive?
deinit{
print("ClassSix deinit")
}
}
var obj6:MyClassSix? = MyClassSix()
var obj5:MyClassFive? = MyClassFive(param: obj6!)
obj6?.cls = obj5
obj6=nil
obj5?.cls
上面所舉的例子滿足了兩種情況,一種是兩類實(shí)例引用的屬性都是Optional值的時候使用weak來解決循環(huán)引用,一種是兩類實(shí)例有一個為非Optional值的時候使用unowned來解決循環(huán)引用,然而還有第三種情況,兩類實(shí)例引用的屬性都為非Optional值的時候,可以使用無主引用與隱式拆包結(jié)合的方式來解決,這也是無主引用最大的應(yīng)用之處,示例如下:
class MyClassSeven{
unowned var cls:MyClassEight
init(param:MyClassEight){
cls = param
}
deinit{
print("ClassSeven deinit")
}
}
class MyClassEight{
var cls:MyClassSeven!
init(){
cls = MyClassSeven(param:self)
}
deinit{
print("ClassEight deinit")
}
}
var obj7:MyClassEight? = MyClassEight()
obj7=nil
除了在兩個類實(shí)例間會產(chǎn)生循環(huán)引用,在閉包中,也可能出現(xiàn)循環(huán)引用,當(dāng)某個類中包含一個閉包屬性,同時這個閉包屬性中又使用了類實(shí)例,則會產(chǎn)生循環(huán)引用,示例如下:
class MyClassNine {
var name:String = "HS"
lazy var closure:()->Void = {
//閉包中使用引用值會使引用+1
print(self.name)
}
deinit{
print("ClassNine deinit")
}
}
var obj9:MyClassNine? = MyClassNine()
obj9?.closure()
obj9=nil
//不會打印析構(gòu)信息
Swift中提供了閉包的捕獲列表來對引用類型進(jìn)行弱引用或者無主引用的轉(zhuǎn)換:
class MyClassNine {
var name:String = "HS"
lazy var closure:()->Void = {
[unowned self]()->Void in
print(self.name)
}
deinit{
print("ClassNine deinit")
}
}
var obj9:MyClassNine? = MyClassNine()
obj9?.closure()
obj9=nil
捕獲列表以中括號標(biāo)識,多個捕獲參數(shù)則使用逗號分隔。
相關(guān)文章
利用Swift實(shí)現(xiàn)各類的CATransition動畫詳解
CATransition動畫主要在過渡時使用,比如兩個頁面層級改變的時候添加一個轉(zhuǎn)場效果。CATransition分為兩類,一類是公開的動畫效果,一類是非公開的動畫效果。這篇文章主要給大家介紹了關(guān)于如何利用Swift實(shí)現(xiàn)各類CATransition動畫的相關(guān)資料,需要的朋友可以參考下。2017-09-09
使用Swift實(shí)現(xiàn)iOS App中解析XML格式數(shù)據(jù)的教程
這篇文章主要介紹了使用Swift實(shí)現(xiàn)iOS App中解析XML格式數(shù)據(jù)的教程,講到了iOS中提供的NSXMLParser和NSXMLParserDelegate兩個API的用法,需要的朋友可以參考下2016-04-04
Swift代碼自定義UIView實(shí)現(xiàn)示例
這篇文章主要為大家介紹了Swift如何自定義UIView的實(shí)現(xiàn)示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步,早日升職加薪2021-10-10
Swift算法之棧和隊(duì)列的實(shí)現(xiàn)方法示例
Swift語言中沒有內(nèi)設(shè)的棧和隊(duì)列,很多擴(kuò)展庫中使用Generic Type來實(shí)現(xiàn)?;蚴顷?duì)列。下面這篇文章就來給大家詳細(xì)介紹了Swift算法之棧和隊(duì)列的實(shí)現(xiàn)方法,需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。2017-03-03
用Swift構(gòu)建一個簡單的iOS郵件應(yīng)用的方法
這篇文章主要介紹了用Swift構(gòu)建一個簡單的iOS郵件應(yīng)用的方法,包括查看和標(biāo)記已讀等基本的郵件應(yīng)用功能,需要的朋友可以參考下2015-07-07

