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")
        }
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容