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

// ARC
//這一章我寫的不好称诗,需要就去看原書

//“Swift 使用自動引用計數(shù)(ARC)機(jī)制來跟蹤和管理你的應(yīng)用程序的內(nèi)存”
//“通常情況下纯赎,Swift 內(nèi)存管理機(jī)制會一直起作用痢畜,你無須自己來考慮內(nèi)存的管理。ARC 會在類的實例不再被使用時,自動釋放其占用的內(nèi)存叛氨《樽校”
//“然而在少數(shù)情況下擂橘,為了能幫助你管理內(nèi)存,ARC 需要更多的摩骨,代碼之間關(guān)系的信息通贞。本章描述了這些情況,并且為你示范怎樣才能使 ARC 來管理你的應(yīng)用程序的所有內(nèi)存恼五。在 Swift 使用 ARC 與在 Obejctive-C 中使用 ARC 非常類似”
//“注意 引用計數(shù)僅僅應(yīng)用于類的實例昌罩。結(jié)構(gòu)體和枚舉類型是值類型,不是引用類型灾馒,也不是通過引用的方式存儲和傳遞茎用。”

//1. 自動引用計數(shù)的工作機(jī)制
//“ARC 會分配一塊內(nèi)存來儲存該實例信息睬罗。內(nèi)存中會包含實例的類型信息轨功,以及這個實例所有相關(guān)的存儲型屬性的值∪荽铮”
//“當(dāng)實例不再被使用時古涧,ARC 釋放實例所占用的內(nèi)存,并讓釋放的內(nèi)存能挪作他用董饰。這確保了不再被使用的實例蒿褂,不會一直占用內(nèi)存空間”
//“當(dāng) ARC 收回和釋放了正在被使用中的實例,該實例的屬性和方法將不能再被訪問和調(diào)用卒暂。實際上啄栓,如果你試圖訪問這個實例,你的應(yīng)用程序很可能會崩潰”
//“為了確保使用中的實例不會被銷毀也祠,ARC 會跟蹤和計算每一個實例正在被多少屬性昙楚,常量和變量所引用。哪怕實例的引用數(shù)為1诈嘿,ARC都不會銷毀這個實例”
//“為了使上述成為可能堪旧,無論你將實例賦值給屬性削葱、常量或變量,它們都會創(chuàng)建此實例的強(qiáng)引用淳梦。之所以稱之為“強(qiáng)”引用析砸,是因為它會將實例牢牢地保持住,只要強(qiáng)引用還在爆袍,實例是不允許被銷毀的”

//2.自動引用計數(shù)實踐

class Person{
    let name:String
    var apartment : Apartment?
    
    init(name:String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}
var reference1:Person?
var reference2:Person?
var reference3:Person?

reference1 = Person(name:"john titol")
//打印 john titol is being initialized
reference2 = reference1
reference3 = reference1
//“現(xiàn)在這一個Person實例已經(jīng)有三個強(qiáng)引用了”
reference1 = nil
reference2 = nil
//“只留下一個強(qiáng)引用首繁,Person實例不會被銷毀”
reference3 = nil
//打印 john titol is being deinitialized

//3.類實例之間的循環(huán)強(qiáng)引用
//“我們可能會寫出一個類實例的強(qiáng)引用數(shù)永遠(yuǎn)不能變成0的代碼。如果兩個類實例互相持有對方的強(qiáng)引用陨囊,因而每個實例都讓對方一直存在弦疮,就是這種情況。這就是所謂的循環(huán)強(qiáng)引用蜘醋⌒踩”
//“你可以通過定義類之間的關(guān)系為弱引用或無主引用,以替代強(qiáng)引用压语,從而解決循環(huán)強(qiáng)引用的問題”

class Apartment{
    let unit:String
    init(unit:String) {
        self.unit = unit
    }
    var tenant:Person?
    deinit {
        print("Apartment \(unit) is being deinitialized")
    }
}

var john:Person?
var unit4A:Apartment?
john = Person(name:"John applesseed")
unit4A = Apartment(unit:"4A")
john!.apartment = unit4A
unit4A!.tenant = John

//“這兩個實例關(guān)聯(lián)后會產(chǎn)生一個循環(huán)強(qiáng)引用啸罢。Person實例現(xiàn)在有了一個指向Apartment實例的強(qiáng)引用,而Apartment實例也有了一個指”“向Person實例的強(qiáng)引用胎食。因此伺糠,當(dāng)你斷開john和unit4A變量所持有的強(qiáng)引用時,引用計數(shù)并不會降為0斥季,實例也不會被 ARC 銷毀:”
john = nil
unit4A = nil
//“當(dāng)你把這兩個變量設(shè)為nil時,沒有任何一個析構(gòu)函數(shù)被調(diào)用累驮。循環(huán)強(qiáng)引用會一直阻止Person和Apartment類實例的銷毀酣倾,這就在你的應(yīng)用程序中造成了內(nèi)存泄漏“ǎ”

//4. 解決實例之間的循環(huán)強(qiáng)引用
//“Swift 提供了兩種辦法用來解決你在使用類的屬性時所遇到的循環(huán)強(qiáng)引用問題:弱引用(weak reference)和無主引用(unowned reference)”
//“弱引用和無主引用允許循環(huán)引用中的一個實例引用而另外一個實例不保持強(qiáng)引用躁锡。這樣實例能夠互相引用而不產(chǎn)生循環(huán)強(qiáng)引用≈檬蹋”
//“當(dāng)其他的實例有更短的生命周期時映之,使用弱引用,也就是說蜡坊,當(dāng)其他實例析構(gòu)在先時杠输。“當(dāng)其他實例有相同的或者更長生命周期時秕衙,請使用無主引用.在上面公寓的例子中蠢甲,很顯然一個公寓在它的生命周期內(nèi)會在某個時間段沒有它的主人,所以一個弱引用就加在公寓類里面据忘,避免循環(huán)引用鹦牛。

//4.1 弱引用
// “弱引用不會對其引用的實例保持強(qiáng)引用搞糕,因而不會阻止 ARC 銷毀被引用的實例。這個特性阻止了引用變?yōu)檠h(huán)強(qiáng)引用曼追。聲明屬性或者變量時窍仰,在前面加上weak關(guān)鍵字表明這是一個弱引用±袷猓”
//“因為弱引用不會保持所引用的實例驹吮,即使引用存在,實例也有可能被銷毀膏燕。因此钥屈,ARC 會在引用的實例被銷毀后自動將其賦值為nil。并且因為弱引用可以允許它們的值在運行時被賦值為nil坝辫,所以它們會被定義為可選類型變量篷就,而不是常量〗Γ”
//“你可以像其他可選值一樣竭业,檢查弱引用的值是否存在,你將永遠(yuǎn)不會訪問已銷毀的實例的引用”
//“注意 當(dāng) ARC 設(shè)置弱引用為nil時及舍,屬性觀察不會被觸發(fā)未辆。”
//“注意 在使用垃圾收集的系統(tǒng)里锯玛,弱指針有時用來實現(xiàn)簡單的緩沖機(jī)制咐柜,因為沒有強(qiáng)引用的對象只會在內(nèi)存壓力觸發(fā)垃圾收集時才被銷毀。但是在 ARC 中攘残,一旦值的最后一個強(qiáng)引用被移除拙友,就會被立即銷毀,這導(dǎo)致弱引用并不適合上面的用途”

//4.2 無主引用
//“和弱引用類似歼郭,無主引用不會牢牢保持住引用的實例遗契。和弱引用不同的是,無主引用在其他實例有相同或者更長的生命周期時使用病曾。你可以在聲明屬性或者變量時牍蜂,在前面加上關(guān)鍵字unowned表示這是一個無主引用”
//“無主引用通常都被期望擁有值。不過 ARC 無法在實例被銷毀后將無主引用設(shè)為nil泰涂,因為非可選類型的變量不允許被賦值為nil鲫竞。”
//“重要 使用無主引用负敏,你必須確保引用始終指向一個未銷毀的實例贡茅。如果你試圖在實例被銷毀后,訪問該實例的無主引用,會觸發(fā)運行時錯誤顶考×藁梗”

class Customer{
    let name:String
    var card:CreditCard?
    init(name:String) {
        self.name = name
    }
    deinit {
        print("\(name) is being deinitializd")
    }
}

class CreditCard{
    let number:UInt64
    unowned let customer:Customer
    init(number:UInt64,customer:Customer) {
        self.number = number
        self.customer = customer
    }
    deinit {
        print("card # \(self.number) is being deinitializd")
    }
}

//“Customer和CreditCard之間的關(guān)系與前面弱引用例子中Apartment和Person的關(guān)系略微不同。在這個數(shù)據(jù)模型中驹沿,一個客戶可能有或者沒有信用卡艘策,但是一張信用卡總是關(guān)聯(lián)著一個客戶。為了表示這種關(guān)系渊季,Customer類有一個可選類型的card屬性朋蔫,但是CreditCard類有一個非可選類型的customer屬性∪春海”

var jack:Customer?
jack = Customer(name:"jack caption")
jack!.card = CreditCard(number:1234_5678_9999,customer:jack!)
jack = nil
//打印 jack caption is being deinitializd
//    card # 123456789999 is being deinitializd

//“你可以通過unowned(unsafe)來聲明不安全無主引用驯妄。如果你試圖在實例被銷毀后,訪問該實例的不安全無主引用合砂,你的程序會嘗試訪問該實例之前所在的內(nèi)存地址青扔,這是一個不安全的操作”

//個人看法:下邊這個有點麻煩,搞不懂了可以去看書翩伪。
//4.3 無主引用以及隱式解析可選屬性
//“上面弱引用和無主引用的例子涵蓋了兩種常用的需要打破循環(huán)強(qiáng)引用的場景微猖。
//Person和Apartment的例子展示了兩個屬性的值都允許為nil,并會潛在的產(chǎn)生循環(huán)強(qiáng)引用缘屹。這種場景最適合用弱引用來解決凛剥。”
//“Customer和CreditCard的例子展示了一個屬性的值允許為nil轻姿,而另一個屬性的值不允許為nil犁珠,這也可能會產(chǎn)生循環(huán)強(qiáng)引用。這種場景最適合通過無主引用來解決互亮∶ぴ鳎”
//“存在著第三種場景,在這種場景中胳挎,兩個屬性都必須有值,并且初始化完成后永遠(yuǎn)不會為nil溺森。在這種場景中慕爬,需要一個類使用無主屬性,而另外一個類使用隱式解析可選屬性”

//5. 閉包引起的循環(huán)強(qiáng)引用
//“循環(huán)強(qiáng)引用還會發(fā)生在當(dāng)你將一個閉包賦值給類實例的某個屬性屏积,并且這個閉包體中又使用了這個類實例時医窿。這個閉包體中可能訪問了實例的某個屬性,例如self.someProperty炊林,或者閉包中調(diào)用了實例的某個方法姥卢,例如self.someMethod()。這兩種情況都導(dǎo)致了閉包“捕獲”self,從而產(chǎn)生了循環(huán)強(qiáng)引用独榴∩妫”
//“循環(huán)強(qiáng)引用的產(chǎn)生,是因為閉包和類相似棺榔,都是引用類型瓶堕。當(dāng)你把一個閉包賦值給某個屬性時,你是將這個閉包的引用賦值給了屬性症歇。實質(zhì)上郎笆,這跟之前的問題是一樣的——兩個強(qiáng)引用讓彼此一直有效。但是忘晤,和兩個類實例不同宛蚓,這次一個是類實例,另一個是閉包设塔∑嗬簦”
//“Swift 提供了一種優(yōu)雅的方法來解決這個問題,稱之為閉包捕獲列表(closure capture list)”

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")
    }
    
}

let heading = HtmlElement(name:"h1")
let defaultText = "some default text"
heading.asHTML = {
    return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
print(heading.asHTML())
//打印 <h1>some default text </h1>

//“注意 asHTML聲明為lazy屬性壹置,因為只有當(dāng)元素確實需要被處理為 HTML 輸出的字符串時竞思,才需要使用asHTML。也就是說钞护,在默認(rèn)的閉包中可以使用self盖喷,因為只有當(dāng)初始化完成以及self確實存在后,才能訪問lazy屬性难咕】问幔”

var parragraph:HtmlElement? = HtmlElement(name:"p",text:"hello world")
print(parragraph!.asHTML())
//“上面寫的HTMLElement類產(chǎn)生了類實例和作為asHTML默認(rèn)值的閉包之間的循環(huán)強(qiáng)引用∮嗟瑁”
//“實例的asHTML屬性持有閉包的強(qiáng)引用暮刃。但是,閉包在其閉包體內(nèi)使用了self(引用了self.name和self.text)爆土,因此閉包捕獲了self椭懊,這意味著閉包又反過來持有了HTMLElement實例的強(qiáng)引用。這樣兩個對象就產(chǎn)生了循環(huán)強(qiáng)引用步势⊙踱”
//“注意 雖然閉包多次使用了self,它只捕獲HTMLElement實例的一個強(qiáng)引用坏瘩≈迅В”
parragraph = nil
//析構(gòu)函數(shù)沒有打印,實例未被銷毀倔矾。

//5. 解決閉包引起的循環(huán)強(qiáng)引用
//“在定義閉包時同時定義捕獲列表作為閉包的一部分妄均,通過這種方式可以解決閉包和類實例之間的循環(huán)強(qiáng)引用柱锹。捕獲列表定義了閉包體內(nèi)捕獲一個或者多個引用類型的規(guī)則”
//“注意 Swift 有如下要求:只要在閉包內(nèi)使用self的成員,就要用self.someProperty或者self.someMethod()(而不只是someProperty或someMethod())。這提醒你可能會一不小心就捕獲了self∮止伲”

//5.1 定義捕獲列表

class  SomeClass{
    var delegate : HtmlElement?
    
    lazy var someClosure:(Int,String)->String = {
        [unowned self,weak delegate = self.delegate!](index:Int,stringToProcess:String)->String in
        // 這里是閉包的函數(shù)體。
        return "\(delegate?.name)"
    }
}

//“在閉包和捕獲的實例總是互相引用并且總是同時銷毀時匹层,將閉包內(nèi)的捕獲定義為無主引用。
//相反的锌蓄,在被捕獲的引用可能會變?yōu)閚il時升筏,將閉包內(nèi)的捕獲定義為弱引用。弱引用總是可選類型瘸爽,并且當(dāng)引用的實例被銷毀后您访,弱引用的值會自動置為nil。這使我們可以在閉包體內(nèi)檢查它們是否存在剪决×橥簦”
//“注意 如果被捕獲的引用絕對不會變?yōu)閚il,應(yīng)該用無主引用柑潦,而不是弱引用享言。”

class TestClass {
    let name : String
    let  text : String?
    
    lazy var testClosure : ()->String = {
        [unowned self] in
        if let text = self.text {
            return "\(text)"
        }else{
            return "\(self.name)"
        }
    }
    init(name:String,text:String? = nil) {
        self.name = name
        self.text = text
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}
var testClass : TestClass? = TestClass(name:"p",text:"hello world")
print(testClass!.testClosure())
//打印 hello world
testClass = nil
// 打印 p is being deinitialized
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渗鬼,一起剝皮案震驚了整個濱河市览露,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌譬胎,老刑警劉巖差牛,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異堰乔,居然都是意外死亡偏化,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門镐侯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侦讨,“玉大人,你說我怎么就攤上這事苟翻〈钌耍” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵袜瞬,是天一觀的道長。 經(jīng)常有香客問我身堡,道長邓尤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮汞扎,結(jié)果婚禮上季稳,老公的妹妹穿的比我還像新娘。我一直安慰自己澈魄,他們只是感情好景鼠,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著痹扇,像睡著了一般铛漓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鲫构,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天浓恶,我揣著相機(jī)與錄音,去河邊找鬼结笨。 笑死包晰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的炕吸。 我是一名探鬼主播伐憾,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赫模!你這毒婦竟也來了树肃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤嘴瓤,失蹤者是張志新(化名)和其女友劉穎扫外,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廓脆,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡筛谚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了停忿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驾讲。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖席赂,靈堂內(nèi)的尸體忽然破棺而出吮铭,到底是詐尸還是另有隱情,我是刑警寧澤颅停,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布谓晌,位于F島的核電站,受9級特大地震影響癞揉,放射性物質(zhì)發(fā)生泄漏纸肉。R本人自食惡果不足惜溺欧,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柏肪。 院中可真熱鬧姐刁,春花似錦、人聲如沸烦味。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谬俄。三九已至柏靶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凤瘦,已是汗流浹背宿礁。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留蔬芥,地道東北人梆靖。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像笔诵,于是被迫代替她去往敵國和親返吻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

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