Swift使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 内存管理机制会一直起作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。然而,在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息。
参考练习代码:
1 import Foundation 2 3 // Swift使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。 4 // 引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。 5 6 // ----------------- Part 1 ------------------------- 7 // 下面的例子展示了自动引用计数的工作机制。 8 class Person { 9 let name: String 10 init(name: String) { 11 self.name = name 12 print("\(name) is being initialized") 13 } 14 deinit { 15 print("\(name) is being deinitialized") 16 } 17 } 18 var reference1: Person? 19 var reference2: Person? 20 var reference3: Person? 21 22 reference1 = Person(name: "John Appleseed") 23 // reference1到Person类的新实例之间建立了一个强引用 24 // 正是因为这一个强引用,ARC 会保证 Person 实例被保持在内存中不被销毁 25 26 reference2 = reference1 27 reference3 = reference2 28 // 现在这一个Person实例已经有三个强引用了 29 30 reference1 = nil 31 reference3 = nil 32 // 给其中两个变量赋值 nil 的方式断开两个强引用(包括最先的那个强引用),只留下一个强引用, Person实例不会被销毁 33 34 reference2 = nil 35 // 最后一个强引用被断开,Person实例被销毁 36 37 38 // ----------------- Part 2 ----------------- 39 // 循环强引用问题,一个类实例的强引用数永远不能变成0 40 class Person { 41 let name: String 42 init(name: String) { self.name = name } 43 var apartment: Apartment? 44 deinit { print("\(name) is being deinitialized") } 45 } 46 class Apartment { 47 let unit: String 48 init(unit: String) { self.unit = unit } 49 var tenant: Person? 50 deinit { print("Apartment \(unit) is being deinitialized") } 51 } 52 var john: Person? 53 var unit4A: Apartment? 54 55 john = Person(name: "John Appleseed") 56 unit4A = Apartment(unit: "4A") 57 // 变量john现在有一个指向Person实例的强引用,而变量unit4A有一个指向Apartment实例的强引用 58 59 john!.apartment = unit4A 60 unit4A!.tenant = john 61 // 这两个实例关联后会产生一个循环强引用 62 63 john = nil 64 unit4A = nil 65 // 当你断开john和unit4A引用时,引用计数并不会减为0,实例也不会被ARC销毁 66 67 68 // ----------------- Part 3 ----------------- 69 // Swift提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。 70 class Person { 71 let name: String 72 init(name: String) { self.name = name } 73 var apartment: Apartment? 74 deinit { print("\(name) is being deinitialized") } 75 } 76 class Apartment { 77 let unit: String 78 init(unit: String) { self.unit = unit } 79 weak var tenant: Person? 80 deinit { print("Apartment \(unit) is being deinitialized") } 81 } 82 83 // 跟之前一样,建立两个变量( john 和 unit4A )之间的强引用,并关联两个实例 84 var john: Person? 85 var unit4A: Apartment? 86 john = Person(name: "John Appleseed") 87 unit4A = Apartment(unit: "4A") 88 john!.apartment = unit4A 89 unit4A!.tenant = john 90 // Person实例依然保持对Apartment实例的强引用,但是Apartment实例只是对Person实例的弱引用。 91 // 这意味着当你断开john变量所保持的强引用时,再也没有指向Person实例的强引用了 92 93 john = nil 94 // 唯一剩下的指向Apartment实例的强引用来自于变量unit4A。如果你断开这个强引用,再也没有指向Apartment实例的强引用了 95 unit4A = nil 96 // 首先断开unit4A的对Apartment实例的强引用,并不会使得Apartment实例销毁,因为此时Person实例依旧有对Apartment实例的强引用 97 98 99 // ----------------- Part 4 -----------------100 // 下面的例子定义了两个类, Customer 和 CreditCard ,模拟了银行客户和客户的信用卡。101 // 这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系可能会造成循环强引用。102 class Customer {103 let name: String104 var card: CreditCard?105 init(name: String) {106 self.name = name107 }108 deinit { print("\(name) is being deinitialized") }109 }110 class CreditCard {111 let number: UInt64112 unowned let customer: Customer113 init(number: UInt64, customer: Customer) {114 self.number = number115 self.customer = customer116 }117 deinit { print("Card #\(number) is being deinitialized") }118 }119 120 var john: Customer?121 john = Customer(name: "John Appleseed")122 john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)123 // Customer 实例持有对 CreditCard 实例的强引用,而 CreditCard 实例持有对 Customer 实例的无主引用。124 125 john = nil126 127 128 // ----------------- Part 4 -----------------129 //Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。130 //Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。131 //存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。132 class Country {133 let name: String134 var capitalCity: City!135 init(name: String, capitalName: String) {136 self.name = name137 self.capitalCity = City(name: capitalName, country: self)138 }139 deinit { print("Country \(name) is being deinitialized") }140 }141 class City {142 let name: String143 unowned let country: Country144 init(name: String, country: Country) {145 self.name = name146 self.country = country147 }148 deinit { print("City \(name) is being deinitialized") }149 }150 var country = Country(name: "Canada", capitalName: "Ottawa")151 print("\(country.name)'s capital city is called \(country.capitalCity.name)")152 // 以上的意义在于你可以通过一条语句同时创建Country和City 的实例,而不产生循环强引用,并且capitalCity的属性能被直接访问,而不需要通过感叹号来展开它的可选值153 country = Country(name: "China", capitalName: "Beijing")154 155 156 // ----------------- Part 5 -----------------157 // 循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例。158 // 这个闭包体中可能访问了实例的某个属性,例如self.someProperty,或者闭包中调用了实例的某个方法,例如 self.someMethod 。159 // 这两种情况都导致了闭包 “捕获" self ,从而产生了循环强引用。160 // 循环强引用的产生,是因为闭包和类相似,都是引用类型161 class HTMLElement {162 let name: String163 let text: String?164 lazy var asHTML: Void -> String = {165 if let text = self.text {166 return "<\(self.name)>\(text) "167 } else {168 return "<\(self.name) />"169 }170 }171 init(name: String, text: String? = nil) {172 self.name = name173 self.text = text174 }175 deinit {176 print("\(name) is being deinitialized")177 }178 }179 180 var heading = HTMLElement(name: "h1")181 let defaultText = "some default text"182 heading.asHTML = {183 return "<\(heading.name)>\(heading.text ?? defaultText) "184 }185 print(heading.asHTML())186 heading = HTMLElement(name: "head")187 188 // HTMLElement类产生了类实例和asHTML默认值的闭包之间的循环强引用。189 var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")190 print(paragraph!.asHTML())191 paragraph = nil192 193 194 // ----------------- Part 6 -----------------195 // Swift提供了一种优雅的方法来解决这个问题,称之为闭包捕获列表(closuer capture list)196 // 在定义闭包时同时定义捕获列表作为闭包的一部分,捕获列表定义了闭包体内捕获一个或者多个引用类型的规则197 // Swift有如下要求:只要在闭包内使用self的成员,就要用self.someProperty(而非someProperty)。这提醒你可能会一不小心就捕获了self。198 // 捕获列表中的每一项都由一对元素组成,一个元素是unowned或weak关键字。199 // 另一个元素是类实例的引用(如self)或初始化过的变量(如self.someProperty)200 class HTMLElement {201 let name: String202 let text: String?203 lazy var asHTML: Void -> String = {204 [unowned self] in205 if let text = self.text {206 return "<\(self.name)>\(text) "207 } else {208 return "<\(self.name) />"209 }210 }211 init(name: String, text: String? = nil) {212 self.name = name213 self.text = text214 }215 deinit {216 print("\(name) is being deinitialized")217 }218 }219 var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")220 print(paragraph!.asHTML())221 paragraph = nil