swift学习笔记之-自动引用计数

时间:2021-05-15 02:52:25

//自动引用计数

import UIKit

/*自动引用计数(Automatic Reference Counting)

防止循环强引用

Swift 使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 内存管理机制会一直起作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。然而,在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息,引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递

自动引用计数的工作机制:ARC会跟踪你所新创建的类的实例的引用数量,只要存在对实例的强引用,该实例就无法被销毁回收内存(类、闭包都是引用类型)

1.当你每次创建一个类的新的实例的时候,ARC 会分配一大块内存用来储存实例的信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。

2.此外,当实例不再被使用时,ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例不会一直占用内存空间。

3.然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。

4.为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性、常量、变量所引用。哪怕实例的引用数为1,ARC都不会销毁这个实例。

5.为了使上述成为可能,无论你将实例赋值给属性、常量或变量,它们都会创建此实例的强引用。之所以称之为“强”引用,是因为它会将实例牢牢地保持住,只要强引用还在,实例是不允许被销毁的

防止循环强引用:(只要切断所有指向实例的强引用,即使存在弱引用或无主引用,则该实例立即被销毁,同时其内部对其他实例的强引用也一起消失)

1.类实例的强引用数永远不能变成0时,即:如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,从而导致引用计数不能变0,就出现循环强引用,无法销毁实例回收内存

==》通过定义类之间的关系为弱引用weak或无主引用unowned,以替代强引用,从而解决循环强引用的问题,弱引用和无主引用允许循环引用中的一个实例引用另外一个实例而不保持强引用。这样实例能够互相引用而不产生循环强引用

a.对于生命周期中会变为nil的实例使用弱引用,它不会阻止 ARC 销毁被引用的实例,声明属性或者变量时,在前面加上weak关键字表明这是一个弱引用,因为弱引用可以没有值,你必须将每一个弱引用声明为可选类型,ARC 会在引用的实例被销毁后自动将可选的变量赋值为nil

b.对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用,无主引用总是被定义为非可选类型(non-optional type)。你可以在声明属性或者变量时,在前面加上关键字unowned表示这是一个无主引用。

c.两个属性都必须有值,并且初始化完成后永远不会为nil时,需要一个类使用无主引用,而另外一个类使用隐式解析可选属性(解包"!",可直接访问该属性)

2.闭包引起的循环强引用:循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时(闭包“捕获”类实例的self)

a.在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用,捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。

b.捕获列表中的每一项都由一对元素组成,一个元素是weak或unowned关键字,另一个元素是类实例的引用(例如self)或初始化过的变量(如delegate = self.delegate!)。这些项在方括号中用逗号分开。

c.在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为无主引用。相反的,在被捕获的引用可能会变为nil时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为nil。这使我们可以在闭包体内检查它们是否存在

*/

class Person {

let name: String

init(name: String) {

self.name = name

print("\(name) is being initialized")

}

deinit {

print("\(name) is being deinitialized")

}

}

var reference1: Person?     //这里只是定义了变量为可选的Person类型,并未创建引用到类Person的实例

var reference2: Person?

var reference3: Person?

reference1 = Person(name: "John Appleseed")     //创建引用

// prints "John Appleseed is being initialized”

reference2 = reference1                         //都引用的同一个实例

reference3 = reference1

reference1 = nil

reference2 = nil

reference3 = nil                                //只有当最后一个对实例的引用被断开时,ARC才会触发析构方法,销毁实例,而回收内存

// 打印 “John Appleseed is being deinitialized”

//============类实例之间的互相引用,均为可选类型,弱引用========

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: Person1?

var unit4A: Apartment?

john = Person1(name: "John Appleseed")

//对Person1实例的引用有2个:john(强引用)和unit4A!.tenant(弱引用),只要切断john的强引用,该实例就会被销毁,同时销毁其属性john!.apartment对其他实例的强引用,

unit4A = Apartment(unit: "4A")

john!.apartment = unit4A

unit4A!.tenant = john

john = nil

// 打印 “John Appleseed is being deinitialized”

unit4A = nil                        //由于再也没有指向Apartment实例的强引用,该实例会被销毁

// 打印 “Apartment 4A is being deinitialized”

//==============一个可选类型,一个必须有值,则无主引用==========

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 john1: Customer?

john1 = Customer(name: "John Appleseed")

john1!.card = CreditCard(number: 1234_5678_9012_3456, customer: john1!)

john1 = nil

//由于Customer实例的强引用只有john1,切断john1的强引用,则Customer实例被销毁,而对CreditCard实例的强引用只有john1!.card,则CreditCard实例也同时被销毁

// 打印 “John Appleseed is being deinitialized”

// 打印 ”Card #1234567890123456 is being deinitialized”

//===============两个类都必须有值,分别用无主引用、隐式解析可选属性=======

class Country {

let name: String

var capitalCity: City!      //capitalCity声明为隐式解析可选类型的属性,默认值为nil,但是不需要展开它的值就能直接访问它。

init(name: String, capitalName: String) {

self.name = name

self.capitalCity = City(name: capitalName, country: self)   //由于capitalCity默认值为nil,一旦Country的实例在构造函数中给name属性赋值后,整个初始化过程就完成了。这意味着一旦name属性被赋值后,Country的构造函数就能引用并传递隐式的self。Country的构造函数在赋值capitalCity时,就能将self作为参数传递给City的构造函数。

}

}

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)")

// 打印 “Canada's capital city is called Ottawa”

//=============类和闭包之间的相互引用=======

class HTMLElement {

let name: String

let text: String?

lazy var asHTML: Void -> String = {

[unowned self] in                   //在闭包中使用了对本类实例的无主引用self

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())

// 打印 “<p>hello, world</p>”

paragraph = nil

// 打印 “p is being deinitialized”