Swift中的循環(huán)??引用

縱然Swift使用ARC(Automatic Reference Counting)為我們打理內存,這并不代表它面對任何情況都足夠聰明趾断。尤其是當對象之間存在相互引用的時候匆浙,更是容易由于reference cycle導致內存無法釋放刃泌。當然,這并非我們本意勺远,只是有時這樣的問題發(fā)生的不甚明顯橙喘。Swift為我們提供了一系列語言機制來處理reference cycle,而我們也應該時刻保持警醒胶逢,避免內存泄漏厅瞎。

import UIKit
class Person {
    let name :String
    var apartment: Apartment?
    var property: Apartment?

    init(name: String) {
        self.name = name
        print("\(name) is being initialized.")
    }
    
    deinit {
        print("\(name) is being deinitialized.")
    }
}
class Apartment {
    let unit :String
    var tenant: Person?
    unowned let owner: Person
    //和strong reference相比饰潜,unowned reference只有一個特別:不會引起對象引用計數(shù)的變化。unowned reference用于解決成員不允許為nil的reference cycle磁奖。

    init(unit: String, owner: Person) {
        self.unit = unit
        self.owner = owner
        print("Apartment \(unit) is being initialized.")
    }
    
    deinit {
        print("Apartment \(unit) is being deinitialized.")
    }
}

//: Strong reference

var ref1 :Person?
var ref2 :Person?
ref1 = Person(name: "Mars")

// count = 2
ref2 = ref1
// count = 1
ref1 = nil
// count = 0
// Mars is being deinitialized.
ref2 = nil   // is being deinitialized 銷毀時調用

var mars :Person? = Person(name: "Mars")

var apt11: Apartment? = Apartment(unit: "11", owner: mars!)
mars!.apartment = apt11
// mars.count = 2
apt11!.tenant = mars

這時囊拜,盡管我們把mars和apt11設置為nil,Person和Apartmetn的deinit也不會被調用了比搭。/因為它們的兩個member(apartment和tenant)是一個strong reference冠跷,指向了彼此,讓對象仍舊“存活”在內存里身诺。
//但是蜜托,mars和apt11已經(jīng)被設置成nil,我們也已經(jīng)無能為力了霉赡。這就是類對象之間的reference cycle橄务。

mars = nil
apt11 = nil
處理對象reference cycle的三種方式
    1:  weak var tenant: Person? “weak reference用于解決成員允許為nil的reference cycle⊙鳎”
    2:  unowned let  “unowned reference用于解決成員不允許為nil的reference cycle蜂挪。”
    3:  unowned reference和implicitly unwrapped optional配合在一起嗓化,用于解決引起reference cycle的兩個成員都不允許為nil的情況棠涮。

class Country {
    let name: String
    var capital: City! // default to nil
    
    init(name: String, capitalName: String) {
        self.name = name
        // Syntax Error!!!
        self.capital = City(name: capitalName, country: self)
    }
    deinit {
        print("Country \(name) is being deinitialized.")
    }
}
class City {
    let name: String
    unowned let country: Country
    
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
    deinit {
        print("City \(name) is being deinitialized.")
    }
}
var cn: Country? = Country(name: "China", capitalName: "Beijing")
var bj: City? = City(name: "Beijing", country: cn!)

cn = nil
bj = nil
 要想構建City時,讓Swift認為Country已經(jīng)構造完刺覆,唯一的做法就是captical有一個默認值nil严肪。至此,對于Capital谦屑,我們有了兩個看似沖突的需求:
 
 對Country的用戶來說驳糯,不能讓他們知道capital是一個optional;
 對Country的設計者來說氢橙,它必須像Optional一樣有一個默認的nil酝枢;
 而解決這種沖突唯一的辦法,就是把capital定義為一個Implicitly Unwrapped Optional (隱式解析可選)悍手。

處理closure和類對象之間的reference cycle

class HTMLElment {
    let name: String
    let text: String?
    
    //“l(fā)azy可以確保一個成員只在類對象被完整初始化過之后隧枫,才能使用∥焦叮”
    lazy var asHTML: (Void) -> String = {
        // text
        // Capture list  由于HTMLElement沒有了strong reference,因此它會被ARC釋放掉协怒,進而asHTML引用的closure也會變成“孤魂野鬼”涝焙,ARC當然也不會放過它。因此孕暇,closure和類對象間的循環(huán)引用問題就解決了仑撞。
        
        //在這里赤兴,關于closure capture list,我們要多說兩點:
        //如果closure帶有完整的類型描述隧哮,capture list必須寫在參數(shù)列表前面桶良;
        //如果我們要在capture list里添加多個成員,用逗號把它們分隔開沮翔;
        
        [unowned self /*, other capture member*/] () -> String in
        if let text = self.text {
            return "<\(self.name)>\(self.text)</\(self.name)>"
        }
        else {
            return "<\(self.name)>"
        }
    }
    //h1是我們定義的strong reference陨帆。Closure作為一個引用類型,它有自己的對象采蚀,因此asHTML也是一個strong reference疲牵。
    //由于asHTML“捕獲”了HTMLElement的self,因此HTMLElement的引用計數(shù)是2榆鼠。
    //當h1為nil時纲爸,asHTML對closure的引用和closure對self的“捕獲”就形成了一個reference cycle。
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
  
    deinit {
        print("\(self.name) is being deinitialized")
    }
}
var h1: HTMLElment? = HTMLElment(name: "h1", text: "Title")
h1?.asHTML
//“當一個類中存在訪問數(shù)據(jù)成員的closure member時妆够,務必要謹慎處理它有可能帶來的reference cycle問題识啦。”
h1 = nil
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末神妹,一起剝皮案震驚了整個濱河市颓哮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灾螃,老刑警劉巖题翻,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異腰鬼,居然都是意外死亡嵌赠,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門熄赡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姜挺,“玉大人,你說我怎么就攤上這事彼硫〈逗溃” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵拧篮,是天一觀的道長词渤。 經(jīng)常有香客問我,道長串绩,這世上最難降的妖魔是什么缺虐? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮礁凡,結果婚禮上高氮,老公的妹妹穿的比我還像新娘慧妄。我一直安慰自己,他們只是感情好剪芍,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布塞淹。 她就那樣靜靜地躺著,像睡著了一般罪裹。 火紅的嫁衣襯著肌膚如雪饱普。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天坊谁,我揣著相機與錄音费彼,去河邊找鬼。 笑死口芍,一個胖子當著我的面吹牛箍铲,可吹牛的內容都是我干的。 我是一名探鬼主播鬓椭,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼颠猴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了小染?” 一聲冷哼從身側響起翘瓮,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裤翩,沒想到半個月后资盅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡踊赠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年呵扛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筐带。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡今穿,死狀恐怖,靈堂內的尸體忽然破棺而出伦籍,到底是詐尸還是另有隱情蓝晒,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布帖鸦,位于F島的核電站芝薇,受9級特大地震影響,放射性物質發(fā)生泄漏作儿。R本人自食惡果不足惜剩燥,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧灭红,春花似錦、人聲如沸口注。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寝志。三九已至娇斑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間材部,已是汗流浹背毫缆。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乐导,地道東北人苦丁。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像物臂,于是被迫代替她去往敵國和親旺拉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內容