Swift 自動引用計數(ARC)

Swift 使用自動引用計數(ARC)這一機制來跟蹤和管理應用程序的內存通常情況下我們不需要去手動釋放內存,因為 ARC 會在類的實例不再被使用時,自動釋放其占用的內存。但在有些時候我們還是需要在代碼中實現內存管理。ARC 功能當每次使用 init() 方法創建一個類的新的實例的時候,ARC 會分配一大塊內存用來儲存實例的信息。內存中會包含實例的類型信息,以及這個實例所有相關屬性的值。當實例不再被使用時,ARC 釋放實例所占用的內存,并讓釋放的內存能挪作他用。為了確保使用中的實例不會被銷毀,ARC 會跟蹤和計算每一個實例正在被多少屬性,常量和變量所引用。實例賦值給屬性、常量或變量,它們都會創建此實例的強引用,只要強引用還在,實例是不允許被銷毀的。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?// 創建Person類的新實例reference1 = Person(name: "Runoob")//賦值給其他兩個變量,該實例又會多出兩個強引用reference2 = reference1reference3 = reference1//斷開第一個強引用reference1 = nil//斷開第二個強引用reference2 = nil//斷開第三個強引用,并調用析構函數reference3 = nil以上程序執行輸出結果為:Runoob 開始初始化Runoob 被析構類實例之間的循環強引用在上面的例子中,ARC 會跟蹤你所新創建的 Person 實例的引用數量,并且會在 Person 實例不再被需要時銷毀它。然而,我們可能會寫出這樣的代碼,一個類永遠不會有0個強引用。這種情況發生在兩個類實例互相保持對方的強引用,并讓對方不被銷毀。這就是所謂的循環強引用。實例下面展示了一個不經意產生循環強引用的例子。例子定義了兩個類: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) 被析構") }}// 兩個變量都被初始化為nilvar runoob: Person?var number73: Apartment?// 賦值runoob = Person(name: "Runoob")number73 = Apartment(number: 73)// 意感嘆號是用來展開和訪問可選變量 runoob 和 number73 中的實例// 循環強引用被創建runoob!.apartment = number73number73!.tenant = runoob// 斷開 runoob 和 number73 變量所持有的強引用時,引用計數并不會降為 0,實例也不會被 ARC 銷毀// 注意,當你把這兩個變量設為nil時,沒有任何一個析構函數被調用。// 強引用循環阻止了Person和Apartment類實例的銷毀,并在你的應用程序中造成了內存泄漏runoob = nilnumber73 = nil解決實例之間的循環強引用Swift 提供了兩種辦法用來解決你在使用類的屬性時所遇到的循環強引用問題:弱引用無主引用弱引用和無主引用允許循環引用中的一個實例引用另外一個實例而不保持強引用。這樣實例能夠互相引用而不產生循環強引用。對于生命周期中會變為nil的實例使用弱引用。相反的,對于初始化賦值后再也不會被賦值為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 數為 \(number)") }}var toc: Module?var list: SubModule?toc = Module(name: "ARC")list = SubModule(number: 4)toc!.sub = listlist!.topic = toctoc = nillist = nil以上程序執行輸出結果為:ARC 主模塊子模塊 topic 數為 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("學生的分數為 \(marks)") }}var module: Student?module = Student(name: "ARC")module!.section = Marks(marks: 98, stname: module!)module = nil以上程序執行輸出結果為:ARC學生的分數為 98閉包引起的循環強引用循環強引用還會發生在當你將一個閉包賦值給類實例的某個屬性,并且這個閉包體中又使用了實例。這個閉包體中可能訪問了實例的某個屬性,例如self.someProperty,或者閉包中調用了實例的某個方法,例如self.someMethod。這兩種情況都導致了閉包 "捕獲" self,從而產生了循環強引用。實例下面的例子為你展示了當一個閉包引用了self后是如何產生一個循環強引用的。例子中定義了一個叫HTMLElement的類,用一種簡單的模型表示 HTML 中的一個單獨的元素:class HTMLElement {? ? ? ? let name: String? ? let text: String?? ? ? ? lazy var asHTML: () -> String = {? ? ? ? if let text = self.text {? ? ? ? ? ? return "<\(self.name)>\(text)"? ? ? ? } else {? ? ? ? ? ? return "<\(self.name) />"? ? ? ? }? ? }? ? ? ? init(name: String, text: String? = nil) {? ? ? ? self.name = name? ? ? ? self.text = text? ? }? ? ? ? deinit {? ? ? ? print("\(name) is being deinitialized")? ? }? ? }// 創建實例并打印信息var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")print(paragraph!.asHTML())HTMLElement 類產生了類實例和 asHTML 默認值的閉包之間的循環強引用。實例的 asHTML 屬性持有閉包的強引用。但是,閉包在其閉包體內使用了self(引用了self.name和self.text),因此閉包捕獲了self,這意味著閉包又反過來持有了HTMLElement實例的強引用。這樣兩個對象就產生了循環強引用。解決閉包引起的循環強引用:在定義閉包時同時定義捕獲列表作為閉包的一部分,通過這種方式可以解決閉包和類實例之間的循環強引用。弱引用和無主引用當閉包和捕獲的實例總是互相引用時并且總是同時銷毀時,將閉包內的捕獲定義為無主引用。相反的,當捕獲引用有時可能會是nil時,將閉包內的捕獲定義為弱引用。如果捕獲的引用絕對不會置為nil,應該用無主引用,而不是弱引用。實例前面的HTMLElement例子中,無主引用是正確的解決循環強引用的方法。這樣編寫HTMLElement類來避免循環強引用:class HTMLElement {? ? ? ? let name: String? ? let text: String?? ? ? ? lazy var asHTML: () -> String = {? ? ? ? [unowned self] in? ? ? ? if let text = self.text {? ? ? ? ? ? return "<\(self.name)>\(text)"? ? ? ? } else {? ? ? ? ? ? return "<\(self.name) />"? ? ? ? }? ? }? ? ? ? init(name: String, text: String? = nil) {? ? ? ? self.name = name? ? ? ? self.text = text? ? }? ? ? ? deinit {? ? ? ? print("\(name) 被析構")? ? }? ? }//創建并打印HTMLElement實例var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")print(paragraph!.asHTML())// HTMLElement實例將會被銷毀,并能看到它的析構函數打印出的消息paragraph = nil以上程序執行輸出結果為:

hello, world

p 被析構

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容