自動引用計數(shù)器(ARC)

Swift 使用自動引用計數(shù)(ARC)這一機制來跟蹤和管理應用程序的內(nèi)存
通常情況下我們不需要去手動釋放內(nèi)存,因為 ARC 會在類的實例不再被使用時求泰,自動釋放其占用的內(nèi)存萄金。
但在有些時候我們還是需要在代碼中實現(xiàn)內(nèi)存管理。
ARC 功能
當每次使用 init() 方法創(chuàng)建一個類的新的實例的時候卧抗,ARC 會分配一大塊內(nèi)存用來儲存實例的信息癞谒。
內(nèi)存中會包含實例的類型信息,以及這個實例所有相關屬性的值刃榨。
當實例不再被使用時弹砚,ARC 釋放實例所占用的內(nèi)存,并讓釋放的內(nèi)存能挪作他用枢希。
為了確保使用中的實例不會被銷毀桌吃,ARC 會跟蹤和計算每一個實例正在被多少屬性,常量和變量所引用苞轿。
實例賦值給屬性茅诱、常量或變量,它們都會創(chuàng)建此實例的強引用搬卒,只要強引用還在瑟俭,實例是不允許被銷毀的。
ARC 實例

class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) 開始初始化")
    }
    deinit {
        print("\(name) 被析構")
    }
}

// 值會被自動初始化為nil契邀,目前還不會引用到Person類的實例
var reference1: Person?
var reference2: Person?
var reference3: Person?

// 創(chuàng)建Person類的新實例
reference1 = Person(name: "Runoob")


//賦值給其他兩個變量摆寄,該實例又會多出兩個強引用
reference2 = reference1
reference3 = reference1

//斷開第一個強引用
reference1 = nil
//斷開第二個強引用
reference2 = nil
//斷開第三個強引用,并調(diào)用析構函數(shù)
reference3 = nil

以上程序執(zhí)行輸出結果為:
Runoob 開始初始化
Runoob 被析構
類實例之間的循環(huán)強引用
在上面的例子中坯门,ARC 會跟蹤你所新創(chuàng)建的 Person 實例的引用數(shù)量微饥,并且會在 Person 實例不再被需要時銷毀它。
然而古戴,我們可能會寫出這樣的代碼欠橘,一個類永遠不會有0個強引用。這種情況發(fā)生在兩個類實例互相保持對方的強引用现恼,并讓對方不被銷毀肃续。這就是所謂的循環(huán)強引用。
實例
下面展示了一個不經(jīng)意產(chǎn)生循環(huán)強引用的例子叉袍。例子定義了兩個類:Person和Apartment痹升,用來建模公寓和它其中的居民:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) 被析構") }
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { print("Apartment #\(number) 被析構") }
}

// 兩個變量都被初始化為nil
var runoob: Person?
var number73: Apartment?

// 賦值
runoob = Person(name: "Runoob")
number73 = Apartment(number: 73)

// 意感嘆號是用來展開和訪問可選變量 runoob 和 number73 中的實例
// 循環(huán)強引用被創(chuàng)建
runoob!.apartment = number73
number73!.tenant = runoob

// 斷開 runoob 和 number73 變量所持有的強引用時,引用計數(shù)并不會降為 0畦韭,實例也不會被 ARC 銷毀
// 注意疼蛾,當你把這兩個變量設為nil時,沒有任何一個析構函數(shù)被調(diào)用艺配。
// 強引用循環(huán)阻止了Person和Apartment類實例的銷毀察郁,并在你的應用程序中造成了內(nèi)存泄漏
runoob = nil
number73 = nil

解決實例之間的循環(huán)強引用
Swift 提供了兩種辦法用來解決你在使用類的屬性時所遇到的循環(huán)強引用問題:
弱引用
無主引用
弱引用和無主引用允許循環(huán)引用中的一個實例引用另外一個實例而不保持強引用衍慎。這樣實例能夠互相引用而不產(chǎn)生循環(huán)強引用。
對于生命周期中會變?yōu)閚il的實例使用弱引用皮钠。相反的稳捆,對于初始化賦值后再也不會被賦值為nil的實例,使用無主引用麦轰。
弱引用實例

class Module {
    let name: String
    init(name: String) { self.name = name }
    var sub: SubModule?
    deinit { print("\(name) 主模塊") }
}

class SubModule {
    let number: Int
    
    init(number: Int) { self.number = number }
    //弱引用
    weak var topic: Module?
    
    deinit { print("子模塊 topic 數(shù)為 \(number)") }
}

var toc: Module?
var list: SubModule?
toc = Module(name: "ARC")
list = SubModule(number: 4)
toc!.sub = list
list!.topic = toc

toc = nil
list = nil

以上程序執(zhí)行輸出結果為:
ARC 主模塊
子模塊 topic 數(shù)為 4
無主引用實例

class Student {
    let name: String
    var section: Marks?
    
    init(name: String) {
        self.name = name
    }
    
    deinit { print("\(name)") }
}
class Marks {
    let marks: Int
//無主引用
    unowned let stname: Student
    
    init(marks: Int, stname: Student) {
        self.marks = marks
        self.stname = stname
    }
    
    deinit { print("學生的分數(shù)為 \(marks)") }
}

var module: Student?
module = Student(name: "ARC")
module!.section = Marks(marks: 98, stname: module!)
module = nil

以上程序執(zhí)行輸出結果為:
ARC
學生的分數(shù)為 98
閉包引起的循環(huán)強引用
循環(huán)強引用還會發(fā)生在當你將一個閉包賦值給類實例的某個屬性乔夯,并且這個閉包體中又使用了實例。這個閉包體中可能訪問了實例的某個屬性款侵,例如self.someProperty末荐,或者閉包中調(diào)用了實例的某個方法,例如self.someMethod新锈。這兩種情況都導致了閉包 "捕獲" self甲脏,從而產(chǎn)生了循環(huán)強引用。
實例
下面的例子為你展示了當一個閉包引用了self后是如何產(chǎn)生一個循環(huán)強引用的妹笆。例子中定義了一個叫HTMLElement的類块请,用一種簡單的模型表示 HTML 中的一個單獨的元素:

class HTMLElement {
    
    let name: String
    let text: String?
    
    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    
    deinit {
        print("\(name) is being deinitialized")
    }
    
}

// 創(chuàng)建實例并打印信息
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

HTMLElement 類產(chǎn)生了類實例和 asHTML 默認值的閉包之間的循環(huán)強引用。
實例的 asHTML 屬性持有閉包的強引用拳缠。但是墩新,閉包在其閉包體內(nèi)使用了self(引用了self.name和self.text),因此閉包捕獲了self窟坐,這意味著閉包又反過來持有了HTMLElement實例的強引用抖棘。這樣兩個對象就產(chǎn)生了循環(huán)強引用。
解決閉包引起的循環(huán)強引用:在定義閉包時同時定義捕獲列表作為閉包的一部分狸涌,通過這種方式可以解決閉包和類實例之間的循環(huán)強引用切省。
弱引用和無主引用
當閉包和捕獲的實例總是互相引用時并且總是同時銷毀時,將閉包內(nèi)的捕獲定義為無主引用帕胆。
相反的朝捆,當捕獲引用有時可能會是nil時,將閉包內(nèi)的捕獲定義為弱引用懒豹。
如果捕獲的引用絕對不會置為nil芙盘,應該用無主引用,而不是弱引用脸秽。
實例
前面的HTMLElement例子中儒老,無主引用是正確的解決循環(huán)強引用的方法。這樣編寫HTMLElement類來避免循環(huán)強引用:

class HTMLElement {
    
    let name: String
    let text: String?
    
    lazy var asHTML: () -> String = {
        [unowned self] in
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    
    deinit {
        print("\(name) 被析構")
    }
    
}

//創(chuàng)建并打印HTMLElement實例
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())

// HTMLElement實例將會被銷毀记餐,并能看到它的析構函數(shù)打印出的消息
paragraph = nil

以上程序執(zhí)行輸出結果為:
<p>hello, world</p>
p 被析構
文章轉(zhuǎn)載自:http://www.runoob.com/swift/swift-arc.html

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驮樊,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌囚衔,老刑警劉巖挖腰,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異练湿,居然都是意外死亡猴仑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門肥哎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辽俗,“玉大人,你說我怎么就攤上這事篡诽⊙缕” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵霞捡,是天一觀的道長。 經(jīng)常有香客問我薄疚,道長碧信,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任街夭,我火速辦了婚禮砰碴,結果婚禮上,老公的妹妹穿的比我還像新娘板丽。我一直安慰自己呈枉,他們只是感情好,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布埃碱。 她就那樣靜靜地躺著猖辫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砚殿。 梳的紋絲不亂的頭發(fā)上啃憎,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機與錄音似炎,去河邊找鬼辛萍。 笑死,一個胖子當著我的面吹牛羡藐,可吹牛的內(nèi)容都是我干的贩毕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼仆嗦,長吁一口氣:“原來是場噩夢啊……” “哼辉阶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤睛藻,失蹤者是張志新(化名)和其女友劉穎启上,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體店印,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡冈在,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了按摘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片包券。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炫贤,靈堂內(nèi)的尸體忽然破棺而出溅固,到底是詐尸還是另有隱情,我是刑警寧澤兰珍,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布侍郭,位于F島的核電站,受9級特大地震影響掠河,放射性物質(zhì)發(fā)生泄漏亮元。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一唠摹、第九天 我趴在偏房一處隱蔽的房頂上張望爆捞。 院中可真熱鬧,春花似錦勾拉、人聲如沸煮甥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽成肘。三九已至,卻和暖如春斧蜕,著一層夾襖步出監(jiān)牢的瞬間艇劫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工惩激, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留店煞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓风钻,卻偏偏與公主長得像顷蟀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子骡技,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容