swift-自動引用計數器

/*
     ? 自動引用計數的工作機制
     ? 自動引用計數實踐
     ? 類實例之間的循環強引用
     ? 解決實例之間的循環強引用
     ? 閉包引起的循環強引用
     ? 解決閉包引起的循環強引用
     
     */
    
    //自動引用計數器的工作機制
    /*當你每次創建一個類的新的實例的時候,ARC 會分配一塊內存來儲存該實例信息。內存中會包含實例的類型信 息,以及這個實例所有相關的存儲型屬性的值。
    此外,當實例不再被使用時,ARC 釋放實例所占用的內存,并讓釋放的內存能挪作他用。這確保了不再被使用的 實例,不會一直占用內存空間。
    然而,當 ARC 收回和釋放了正在被使用中的實例,該實例的屬性和方法將不能再被訪問和調用。實際上,如果你 試圖訪問這個實例,你的應用程序很可能會崩潰。
    為了確保使用中的實例不會被銷毀,ARC 會跟蹤和計算每一個實例正在被多少屬性,常量和變量所引用。哪怕實 例的引用數為1,ARC都不會銷毀這個實例。
    為了使上述成為可能,無論你將實例賦值給屬性、常量或變量,它們都會創建此實例的強引用。之所以稱之
    為“強”引用,是因為它會將實例牢牢地保持住,只要強引用還在,實例是不允許被銷毀的。*/
    
    //自動引用計數實踐
    class Person {
    
        let name: String
        init(name: String) {
            self.name = name
            print("\(name) is being initialized")
        }
        deinit {
            print("\(name) is being deinitialized")
        }
        
    }
    
   // Person 類有一個構造函數,此構造函數為實例的 name 屬性賦值,并打印一條消息以表明初始化過程生效。 on 類也擁有一個析構函數,這個析構函數會在實例被銷毀時打印一條消息。
    var reference1: Person?
    var reference2: Person?
    var reference3: Person?
    
    reference1 = Person(name: "John Appleseed")
   // 應當注意到當你調用 Person 類的構造函數的時候, “John Appleseed is being initialized” 會被打印出來。由 此可以確定構造函數被執行。
    reference2 = reference1
    reference3 = reference1
    
    //如果你通過給其中兩個變量賦值 nil 的方式斷開兩個強引用(包括最先的那個強引用),只留下一個強引用, rson 實例不會被銷毀:
    reference1 = nil
    reference2 = nil
    //在你清楚地表明不再使用這個 Person 實例時,即第三個也就是最后一個強引用被斷開時,ARC 會銷毀它:
    
    reference3 = nil
    // 打印 “John Appleseed is being deinitialized”
    
    
    //類實例之間的循環引用
    //在上面的例子中,ARC 會跟蹤你所新創建的 Person 實例的引用數量,并且會在 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")
    
   // 不幸的是,這兩個實例關聯后會產生一個循環強引用。 Person 實例現在有了一個指向 Apartment 實例的強引 用,而 Apartment 實例也有了一個指向 Person 實例的強引用。因此,當你斷開 john 和 unit4A 變量所持有的強 引用時,引用計數并不會降為 0 ,實例也不會被 ARC 銷毀:
    john = nil
    unit4A = nil
    //注意,當你把這兩個變量設為 nil 時,沒有任何一個析構函數被調用。循環強引用會一直阻止 Person 和 Apartme nt 類實例的銷毀,這就在你的應用程序中造成了內存泄漏。
    
    
    //解決實例之間的循環引用
    //弱引用:聲明屬性或者變量時,在前面加上 weak 關鍵字表明這是一個弱引用。
    
   // 然后跟之前一樣,建立兩個變量( john 和 unit4A )之間的強引用,并關聯兩個實例:
    
    
    //無主引用
    //和弱引用類似,無主引用不會牢牢保持住引用的實例。和弱引用不同的是,無主引用在其他實例有相同或者更長 的生命周期時使用。你可以在聲明屬性或者變量時,在前面加上關鍵字 unowned 表示這是一個無主引用。
    //重要
   // 使用無主引用,你必須確保引用始終指向一個未銷毀的實例。
   // 如果你試圖在實例被銷毀后,訪問該實例的無主引用,會觸發運行時錯誤。
    
    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 的構造函數接受一個 Country 實例作為參數,并且將實例保存到 country 屬性。
    //Country 的構造函數調用了 City 的構造函數。然而,只有 Country 的實例完全初始化后, Country 的構造函數 才能把 self 傳給 City 的構造函數
    //為了滿足這種需求,通過在類型結尾處加上感嘆號( City! )的方式,將 Country 的 capitalCity 屬性聲明為隱 式解析可選類型的屬性。這意味著像其他可選類型一樣, capitalCity 屬性的默認值為 nil ,但是不需要展開它 的值就能訪問它
   // 由于 capitalCity 默認值為 nil ,一旦 Country 的實例在構造函數中給 name 屬性賦值后,整個初始化過程就完 成了。這意味著一旦 name 屬性被賦值后, Country 的構造函數就能引用并傳遞隱式的 self 。 Country 的構造函 數在賦值 capitalCity 時,就能將 self 作為參數傳遞給 City 的構造函數。
    //以上的意義在于你可以通過一條語句同時創建 Country 和 City 的實例,而不產生循環強引用,并且 的屬性能被直接訪問,而不需要通過感嘆號來展開它的可選值:
    var country = Country(name: "Canada", capitalName: "Ottawa")
    print("\(country.name)'s capital city is called \(country.capitalCity.name)")
    
    
    
    //閉包引起的循環引用
    //循環強引用還會發生在當你將一個閉包賦值給類實例的某個屬性,并且這個閉包體中又使用了這個類實例時。這個閉包體中可能訪問了實例的某個屬性,例如 self.someProperty ,或者閉包中調用了實例的某個方法,例如 self.someMethod() 。這兩種情況都導致了閉包“捕獲” self ,從而產生了循環強引用。
    
    //循環強引用的產生,是因為閉包和類相似,都是引用類型。
    
    //下面的例子為你展示了當一個閉包引用了 self 后是如何產生一個循環強引用的。例子中定義了一個叫 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")
//            }
//            
//        }
    
    //解決閉包引起的循環引用
    //只要在閉包內使用 self 的成員,就要用 self.someProperty 或者 self.someMethod() (而 不只是 someProperty 或 someMethod() )。這提醒你可能會一不小心就捕獲了 self 。

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

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

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

推薦閱讀更多精彩內容