自动引用计数(ARC)(学习笔记)
环境Xcode 11.0 beta4 swift 5.1
- 前言
- SWift 用ARC追踪和管理应用程序的内存使用情况,与OC中的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") } } // 定义有一个 Person? 类型实例 var reference1: Person var reference2: Person var reference3: Person reference1 = Person(name: "John Appleseed") // Prints "John Appleseed is being initialized" reference2 = reference1 reference3 = reference1 // 3 strong references reference1 = nil reference2 = nil // 1 strong reference remain 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: Persong? deinit { print("Apartment \(unit) is being deinitialized") } } // 定义两个可行变量 var john: Person? var unit4A: Apartment? // 创建两个实例 john = Person(name: "John Appleseed") unit4A = Apartment(unit: "4A") // 在创建和赋值后之间的引用关系如下图
现在可以将两个实例关联起来
john!.appartment = unit4A unit4A!.tenant = john // 之间引用关系如下
将两个变量置 nil
john = nil unit4A = nil // 二者的引用关系图如下,此时两个实例仍然有强引用在,因此不会被销毁
- 类实例间强引用循环的解决
- Swift 提供两种解决方法:
weak
引用 和uNowned
引用,这两个引用不会让实例间产生强引用 weak
主要适用一个实例生命周期较短的那个,弱引用通常会在运行的时候赋值为nil,因此通常将其声明为可选类型的变量而非常量uNowned
一个实例有同样的生命周期或者更长的生命周期- 当给一个
weak
引用置 nil 时,不会触发 属性观察器 将上面的示例用
weak
改造如下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"
uNowned
引用与weak
相似都不产生强引用,不同的是uNowned
引用总是有一个值,ARC不会自动将其置 nil,这意味着定义时要使用非可选类型- 只有在确认引用一起在引用实例没有被释放,才能使用
uNowned
引用 - 如果试图在
uNowned
实例被释放后访问,将会产生运行时错误 以下是
Customer
CreditCard
示例,Customer
可以没有CreditCard
,但CreditCard
一定属于一个Customer
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 // 引用关系如下
* 上面的
uNowned
示例是安全的引用,如果你要禁止运行时安全检查(例如出于性能考虑)可以用uNowned(unsafe)
,那这样开发者有责任检查代码的安全性;
如果要访问已经销毁的unsafe uNowned
引用时,将会访问之前的内存位置,是不安全的操作uNowned
引用与隐式解包属性- 上面的两种解决方法基本包含大部分的情况,但还有第三种情况,就是属性都不能为nil,此时就要结合使用
uNowned
引用与隐式解包
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" // 这里有几点说明 // 1. City 的初始化是在 Country 初始化里,按照前面所说的两阶段初始化是不行,因为 City 初始化里用到 Country,此处的解决办法是隐式解包 // 这意味着属性 capitalCity 有默认初始值 nil, 只是访问时不需要解包 // 2. 因此 Country 的初始化在 name 赋值完成时就初始化完毕,后面才可以将 self 作为参数传入
- 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") } } let heading = HTMLElement(name: "h1") let defaultText = "some default text" heading.asHTML = { return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>" } print(heading.asHTML()) // Prints "<h1>some default text</h1>" var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world") print(paragraph!.asHTML()) // Prints "<p>hello, world</p>" // 创建一个新实例,引用 示意图如下 paragraph = nil // 此时实例并不会被销毁
- 解决闭包的强引用
定义一个捕获列表,写在参数列表和返回值之前
lazy var someClosure = { [uNowned self, weak delegate = self.delegate] (index: Int, stringToProcess: String) -> String in // closure body goes here } // 如果没有参数列表,没有返回值 lazy var someClosure = { [uNowned self, weak delegate = self.delegate] in // closure body goes here }
uNowned
weak
引用,两者的区别如上面所说的一样class HTMLElement { let name: String let text: String? lazy var asHTML: () -> String = { [uNowned self] in 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>" // 创建一个新实例,引用 示意图如下 paragraph = nil // Prints "p is being deinitialized"
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。