swift-自動引用計數(shù)器

/*
     ? 自動引用計數(shù)的工作機制
     ? 自動引用計數(shù)實踐
     ? 類實例之間的循環(huán)強引用
     ? 解決實例之間的循環(huán)強引用
     ? 閉包引起的循環(huán)強引用
     ? 解決閉包引起的循環(huán)強引用
     
     */
    
    //自動引用計數(shù)器的工作機制
    /*當你每次創(chuàng)建一個類的新的實例的時候,ARC 會分配一塊內存來儲存該實例信息脱羡。內存中會包含實例的類型信 息,以及這個實例所有相關的存儲型屬性的值免都。
    此外,當實例不再被使用時,ARC 釋放實例所占用的內存,并讓釋放的內存能挪作他用。這確保了不再被使用的 實例,不會一直占用內存空間脓规。
    然而,當 ARC 收回和釋放了正在被使用中的實例,該實例的屬性和方法將不能再被訪問和調用业舍。實際上,如果你 試圖訪問這個實例,你的應用程序很可能會崩潰。
    為了確保使用中的實例不會被銷毀,ARC 會跟蹤和計算每一個實例正在被多少屬性,常量和變量所引用态罪。哪怕實 例的引用數(shù)為1,ARC都不會銷毀這個實例。
    為了使上述成為可能,無論你將實例賦值給屬性复颈、常量或變量,它們都會創(chuàng)建此實例的強引用耗啦。之所以稱之
    為“強”引用,是因為它會將實例牢牢地保持住,只要強引用還在,實例是不允許被銷毀的。*/
    
    //自動引用計數(shù)實踐
    class Person {
    
        let name: String
        init(name: String) {
            self.name = name
            print("\(name) is being initialized")
        }
        deinit {
            print("\(name) is being deinitialized")
        }
        
    }
    
   // Person 類有一個構造函數(shù),此構造函數(shù)為實例的 name 屬性賦值,并打印一條消息以表明初始化過程生效帜讲。 on 類也擁有一個析構函數(shù),這個析構函數(shù)會在實例被銷毀時打印一條消息椒拗。
    var reference1: Person?
    var reference2: Person?
    var reference3: Person?
    
    reference1 = Person(name: "John Appleseed")
   // 應當注意到當你調用 Person 類的構造函數(shù)的時候, “John Appleseed is being initialized” 會被打印出來获黔。由 此可以確定構造函數(shù)被執(zhí)行在验。
    reference2 = reference1
    reference3 = reference1
    
    //如果你通過給其中兩個變量賦值 nil 的方式斷開兩個強引用(包括最先的那個強引用),只留下一個強引用, rson 實例不會被銷毀:
    reference1 = nil
    reference2 = nil
    //在你清楚地表明不再使用這個 Person 實例時,即第三個也就是最后一個強引用被斷開時,ARC 會銷毀它:
    
    reference3 = nil
    // 打印 “John Appleseed is being deinitialized”
    
    
    //類實例之間的循環(huán)引用
    //在上面的例子中,ARC 會跟蹤你所新創(chuàng)建的 Person 實例的引用數(shù)量,并且會在 Person 實例不再被需要時銷毀它腋舌。
    class Person1 {
    
        let name: String
        init(name: String) {
            self.name = name
        }
    var apartment: Apartment?
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    
    class Apartment {
    
        let unit: String
        init(unit: String) {
            self.unit = unit
        }
       weak var tenant:Person1?
        deinit {
             print("Apartment \(unit) is being deinitialized")
        }
        
    }
    
    var john: Person?
    var unit4A: Apartment?
    
    john = Person(name: "John Appleseed")
    unit4A = Apartment(unit: "4A")
    
   // 不幸的是,這兩個實例關聯(lián)后會產生一個循環(huán)強引用。 Person 實例現(xiàn)在有了一個指向 Apartment 實例的強引 用,而 Apartment 實例也有了一個指向 Person 實例的強引用赞辩。因此,當你斷開 john 和 unit4A 變量所持有的強 引用時,引用計數(shù)并不會降為 0 ,實例也不會被 ARC 銷毀:
    john = nil
    unit4A = nil
    //注意,當你把這兩個變量設為 nil 時,沒有任何一個析構函數(shù)被調用刨沦。循環(huán)強引用會一直阻止 Person 和 Apartme nt 類實例的銷毀,這就在你的應用程序中造成了內存泄漏。
    
    
    //解決實例之間的循環(huán)引用
    //弱引用:聲明屬性或者變量時,在前面加上 weak 關鍵字表明這是一個弱引用召庞。
    
   // 然后跟之前一樣,建立兩個變量( john 和 unit4A )之間的強引用,并關聯(lián)兩個實例:
    
    
    //無主引用
    //和弱引用類似,無主引用不會牢牢保持住引用的實例篮灼。和弱引用不同的是,無主引用在其他實例有相同或者更長 的生命周期時使用。你可以在聲明屬性或者變量時,在前面加上關鍵字 unowned 表示這是一個無主引用徘禁。
    //重要
   // 使用無主引用,你必須確保引用始終指向一個未銷毀的實例送朱。
   // 如果你試圖在實例被銷毀后,訪問該實例的無主引用,會觸發(fā)運行時錯誤。
    
    class Customer {
    
        let name:String
        var card:CreditCard?
        init(name: String) {
            self.name = name
        }
        deinit {
            print("\(name) is being deinitialized")
        }
    }
    
        class CreditCard {
            let number:NSInteger
            unowned let customer: Customer
            init(number:NSInteger , customer: Customer) {
                self.number = number
                self.customer = customer
            }
            deinit {
                print("Card #\(number) is being deinitialized")
            
            }
        }
    
    var john1: Customer?
    john1 = Customer(name: "John Appleseed")
    john1!.card = CreditCard(number: 1234_5678_9012_3456, customer: john1!)

    
    //無主引用以及隱式解析可選屬性
    //兩個屬性都必須有值,并且初始化完成后永遠不會為 nil 炮沐。在這種場 景中,需要一個類使用無主屬性,而另外一個類使用隱式解析可選屬性大年。

    class Country {
    
        let name: String
        var capitalCity: City!
        init(name: String, capitalName: String) {
            self.name = name
            self.capitalCity = City(name: capitalName , country: self)
        }
    }
    
    class City{
    
        let name: String
        unowned let country:Country
        init(name: String ,country: Country) {
            self.name = name
            self.country = country
        }
        
    }
    
    //為了建立兩個類的依賴關系, City 的構造函數(shù)接受一個 Country 實例作為參數(shù),并且將實例保存到 country 屬性翔试。
    //Country 的構造函數(shù)調用了 City 的構造函數(shù)复旬。然而,只有 Country 的實例完全初始化后, Country 的構造函數(shù) 才能把 self 傳給 City 的構造函數(shù)
    //為了滿足這種需求,通過在類型結尾處加上感嘆號( City! )的方式,將 Country 的 capitalCity 屬性聲明為隱 式解析可選類型的屬性。這意味著像其他可選類型一樣, capitalCity 屬性的默認值為 nil ,但是不需要展開它 的值就能訪問它
   // 由于 capitalCity 默認值為 nil ,一旦 Country 的實例在構造函數(shù)中給 name 屬性賦值后,整個初始化過程就完 成了驹碍。這意味著一旦 name 屬性被賦值后, Country 的構造函數(shù)就能引用并傳遞隱式的 self 。 Country 的構造函 數(shù)在賦值 capitalCity 時,就能將 self 作為參數(shù)傳遞給 City 的構造函數(shù)粹庞。
    //以上的意義在于你可以通過一條語句同時創(chuàng)建 Country 和 City 的實例,而不產生循環(huán)強引用,并且 的屬性能被直接訪問,而不需要通過感嘆號來展開它的可選值:
    var country = Country(name: "Canada", capitalName: "Ottawa")
    print("\(country.name)'s capital city is called \(country.capitalCity.name)")
    
    
    
    //閉包引起的循環(huán)引用
    //循環(huán)強引用還會發(fā)生在當你將一個閉包賦值給類實例的某個屬性,并且這個閉包體中又使用了這個類實例時庞溜。這個閉包體中可能訪問了實例的某個屬性,例如 self.someProperty ,或者閉包中調用了實例的某個方法,例如 self.someMethod() 碑定。這兩種情況都導致了閉包“捕獲” self ,從而產生了循環(huán)強引用延刘。
    
    //循環(huán)強引用的產生,是因為閉包和類相似,都是引用類型。
    
    //下面的例子為你展示了當一個閉包引用了 self 后是如何產生一個循環(huán)強引用的碘赖。例子中定義了一個叫 t 的類,用一種簡單的模型表示 HTML 文檔中的一個單獨的元素:
//        class HTMLElement {
//            let name: String
//            let text: String?
//            lazy var asHTML: (Void) -> 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")
//            }
//            
//        }
    
    //解決閉包引起的循環(huán)引用
    //只要在閉包內使用 self 的成員,就要用 self.someProperty 或者 self.someMethod() (而 不只是 someProperty 或 someMethod() )普泡。這提醒你可能會一不小心就捕獲了 self 。

    //定義捕獲列表 
   // 捕獲列表中的每一項都由一對元素組成,一個元素是 weak 或 unowned 關鍵字,另一個元素是類實例的引用(例如 self )或初始化過的變量(如 delegate = self.delegate! )歧匈。這些項在方括號中用逗號分開砰嘁。
    //如果閉包有參數(shù)列表和返回類型,把捕獲列表放在它們前面:
    
    //lazy var someClosure: (NSInteger ,String) -> String = {
    
      //  [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in // 這里是閉包的函數(shù)體
   // }
    
    //如果閉包沒有指明參數(shù)列表或者返回類型,即它們會通過上下文推斷,那么可以把捕獲列表和關鍵字 in 放在閉包 最開始的地方:
    //lazy var someClosure: Void -> String = {
     //   [unowned self, weak delegate = self.delegate!] in // 這里是閉包的函數(shù)體
    //}
    
    //弱引用和無主引用

    //在閉包和捕獲的實例總是互相引用并且總是同時銷毀時,將閉包內的捕獲定義為 無主引用 。
    //相反的,在被捕獲的引用可能會變?yōu)?nil 時,將閉包內的捕獲定義為 弱引用 斟冕。弱引用總是可選類型,并且當引用 的實例被銷毀后,弱引用的值會自動置為 nil 板祝。這使我們可以在閉包體內檢查它們是否存在。

    
    //前面的 HTMLElement 例子中,無主引/弱引用用是正確的解決循環(huán)強引用的方法孤里。這樣編寫 HTMLElement 類來避免循環(huán)強 引用:
    class HTMLElement {
        let name: String
        let text: String?
        lazy var asHTML: (Void) -> String = {
           [unowned self] in//[weak 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) is being deinitialized")
        }
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末捌袜,一起剝皮案震驚了整個濱河市虏等,隨后出現(xiàn)的幾起案子弄唧,更是在濱河造成了極大的恐慌候引,老刑警劉巖敦跌,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柠傍,死亡現(xiàn)場離奇詭異,居然都是意外死亡惧笛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門拜效,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拂檩,“玉大人嘲碧,你說我怎么就攤上這事⊥椋” “怎么了履婉?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵毁腿,是天一觀的道長。 經常有香客問我已烤,道長,這世上最難降的妖魔是什么稍计? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任臣嚣,我火速辦了婚禮,結果婚禮上硅则,老公的妹妹穿的比我還像新娘。我一直安慰自己怎虫,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饥努,像睡著了一般八回。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上溶浴,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天士败,我揣著相機與錄音,去河邊找鬼谅将。 笑死,一個胖子當著我的面吹牛饥臂,可吹牛的內容都是我干的似踱。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼囚戚,長吁一口氣:“原來是場噩夢啊……” “哼狞洋!你這毒婦竟也來了?” 一聲冷哼從身側響起庐橙,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎转培,沒想到半個月后浆竭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡删窒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年肌索,在試婚紗的時候發(fā)現(xiàn)自己被綠了特碳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡站宗,死狀恐怖益愈,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情或辖,我是刑警寧澤枣接,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布但惶,位于F島的核電站,受9級特大地震影響膀曾,放射性物質發(fā)生泄漏。R本人自食惡果不足惜财喳,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扎瓶。 院中可真熱鬧泌枪,春花似錦、人聲如沸误证。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慈鸠。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稽亏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工胖腾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咸作。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓记罚,卻偏偏與公主長得像桐智,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子说庭,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容