自动引用计数

来源:互联网 发布:澳大利亚mac口红便宜么 编辑:程序博客网 时间:2024/05/17 06:18

ARC 如何工作

    每当你产生一个对象,ARC分配一大块控件去存储这个对象。    除此之外,当一个对象是不在需要,ARC 将会收回这个实例对象所占的空间。    如果一个对象的空间被释放掉,但是你任然通过对象的引用调用方法或者属性,你的程序将要崩溃。    为了不让对象被释放掉 ARC将会追踪有多少属性、常量、变量正在引用这个对象。只要有一个在引用他 ARC 将不会释放这个对象。    因此当年给属性、常量或者变量赋值的时候,应该确保强引用这个对象。例子:class Person {    let name: String    init(name: String) {        self.name = name        print("\(name) is being initialized")    }    deinit {        print("\(name) is being deinitialized")    }}定义三个变量,因为是可选类型,所以自动赋值为 nilvar reference1: Person?var reference2: Person?var reference3: Person?//引用计数变为1reference1 = Person(name: "John Appleseed")// Prints "John Appleseed is being initialized"//引用计数变为3reference2 = reference1reference3 = reference1//引用计数变为1reference1 = nilreference2 = nil//引用计数变为0reference3 = nil// Prints "John Appleseed is being deinitialized"

循环引用

    当两个对象相互强引用对方,将会造成循环引用的情况,这会导致空间浪费。我们可以用弱引用或者打破这种情况。    下面是循环引用的例子:class Person {    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 }    var tenant: Person?    deinit { print("Apartment \(unit) is being deinitialized") }}var john: Person?var unit4A: Apartment?john = Person(name: "John Appleseed")unit4A = Apartment(unit: "4A")引用情况:

这里写图片描述

john!.apartment = unit4Aunit4A!.tenant = john现在的引用情况:

这里写图片描述

    john = nil    unit4A = nil

这里写图片描述

这样导致了循环引用,导致空间无法释放。

解决两个类的循环引用

    Swift 用两种方式来解决循环引用问题既弱引用和无主引用。    弱引用和无主引用确保一个对象在循环引用中没有强引用另一个对象。当一个对象的存在时间小于另一个对象,我们可以用弱引用。当另一个对象有相同的生存时间或者更长我们应该用无主引用来打破这个循环。

弱引用

  弱引用不增加对象的引用计数,当引用对象被 ARC 释放的时候,弱引用的值将会变为 nil。因此弱引用应该被声明为变量。要注意的是当 ARC把弱引用变为 nil 的时候,属性观察者不会被调用。class Person {    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: Person?    deinit { print("Apartment \(unit) is being deinitialized") }}var john: Person?var unit4A: Apartment?john = Person(name: "John Appleseed")unit4A = Apartment(unit: "4A")john!.apartment = unit4Aunit4A!.tenant = john

这里写图片描述

 john = nil// Prints "John Appleseed is being deinitialized" unit4A = nil// Prints "Apartment 4A is being deinitialized"

这里写图片描述

无主引用

    无主应用不会增加对象的引用计数,当所引用的对象被释放的时候, ARC 不会吧无主引用变为 nil。因此无主引用不能为可选值class Customer {    let name: String    var card: CreditCard?    init(name: String) {        self.name = name    }    deinit { print("\(name) is being deinitialized") }}class CreditCard {    let number: UInt64    unowned let customer: Customer    init(number: UInt64, customer: Customer) {        self.number = number        self.customer = customer    }    deinit { print("Card #\(number) is being deinitialized") }}var john: Customer?john = Customer(name: "John Appleseed")john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

这里写图片描述

john = nil// Prints "John Appleseed is being deinitialized"// Prints "Card #1234567890123456 is being deinitialized"

这里写图片描述

 上面是安全的无主引用,我们可以用 unowned(unsafe)来定义不安全的无主引用, 这意味你有责任来保证代码的安全。否则当对象被释放后,你任然用无主引用来引用对象 所在空间,会导致不可预料的错误。

无主引用和含蓄的解包

     Person 和 Apartment 列子中的情况是两个互相引用的属性都可以是 nil,因此,我们可以用弱引用。    Customer 和 CreditCard 例子中的情况是其中有一个可以为 nil,因此我们可以用无主引用来打破循环。    但是还有一种情况是两个属性在初始化完成后都不可以为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    }}var country = Country(name: "Canada", capitalName: "Ottawa")print("\(country.name)'s capital city is called \(country.capitalCity.name)")// Prints "Canada's capital city is called Ottawa"

闭包的强引用循环

    当闭包是类的一个属性,并且闭包捕获了这个类本身,那么将会发生循环引用。当在闭包体里引用本实例的属性时,例如 self.someProperty 或者闭包调用一个方法例如 self.someMethod()。上面的情况都会导致闭包捕获本实例对象,导致循环引用。    闭包是一个引用类型,当把闭包赋值给一个属性,那么属性强引用闭包。    Swift中可以用闭包捕获列表来解决这个问题    下面的例子展示如何利用导致循环引用: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")    }}var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")print(paragraph!.asHTML())// Prints "<p>hello, world</p>"

这里写图片描述

导致了循环引用

解决闭包强引用循环问题

我们可以定义一个捕获列表,来说明闭包和他捕获对象引用的关系。

第一捕获列表

捕获列表的每一项都是 weak 或者unowned 关键字和一个类的实例变量(such as self)或者一个变量的初始化(such as delegate = self.delegate!)放置捕获列表在闭包的参数列表和返回值前:lazy var someClosure: (Int, String) -> String = {    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in    // closure body goes here}

弱引用和无主引用

当闭包和其捕获的引用将要一直引用彼此并且一同被回收,我们可以用无主引用。相反的,如果一个对象在一些情况下可能成为 nil,用弱引用。
0 0
原创粉丝点击