Swift学习笔记-ARC

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: String
104     var card: CreditCard?
105     init(name: String) {
106         self.name = name
107     }
108     deinit { print("(name) is being deinitialized") }
109 }
110 class CreditCard {
111     let number: UInt64
112     unowned let customer: Customer
113     init(number: UInt64, customer: Customer) {
114         self.number = number
115         self.customer = customer
116     }
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 = nil
126 
127 
128 // ----------------- Part 4 -----------------
129 //Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
130 //Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。
131 //存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。
132 class Country {
133     let name: String
134     var capitalCity: City!
135     init(name: String, capitalName: String) {
136         self.name = name
137         self.capitalCity = City(name: capitalName, country: self)
138     }
139     deinit { print("Country (name) is being deinitialized") }
140 }
141 class City {
142     let name: String
143     unowned let country: Country
144     init(name: String, country: Country) {
145         self.name = name
146         self.country = country
147     }
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: String
163     let text: String?
164     lazy var asHTML: Void -> String = {
165         if let text = self.text {
166         return "<(self.name)>(text)</(self.name)>"
167         } else {
168             return "<(self.name) />"
169         }
170     }
171     init(name: String, text: String? = nil) {
172         self.name = name
173         self.text = text
174     }
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)</(heading.name)>"
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 = nil
192 
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: String
202     let text: String?
203     lazy var asHTML: Void -> String = {
204         [unowned self] in
205         if let text = self.text {
206             return "<(self.name)>(text)</(self.name)>"
207         } else {
208             return "<(self.name) />"
209         }
210     }
211     init(name: String, text: String? = nil) {
212         self.name = name
213         self.text = text
214     }
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
原文地址:https://www.cnblogs.com/xjy-123/p/5162936.html