自动引用计数

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")
    }
}
定义三个变量,因为是可选类型,所以自动赋值为 nil
var reference1: Person?
var reference2: Person?
var reference3: Person?

//引用计数变为1
reference1 = Person(name: "John Appleseed")
// Prints "John Appleseed is being initialized"

//引用计数变为3
reference2 = reference1
reference3 = reference1

//引用计数变为1
reference1 = nil
reference2 = nil

//引用计数变为0
reference3 = 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 = unit4A
unit4A!.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 = unit4A
unit4A!.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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值