Swift之深入解析Xcode13对Swift对象生命周期的优化
Posted Forever_wj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift之深入解析Xcode13对Swift对象生命周期的优化相关的知识,希望对你有一定的参考价值。
- 在 Xcode13 中,在 Build Setting 中,新增 Optimize Object Lifetimes 编译选项,默认是关闭的,Apple 建议将该选项设置为 YES,打开此优化项,可以减小 Swift 对象的生命周期,这样就可以更高效的使用内存。
- 在修改编译器设置为 YES 之前,先了解下 Swift 中的 ARC,需要注意以下几点:
-
- 对象的生命周期从 init() 开始到最后一次使用结束;
-
- 在生命周期结束之后,ARC 会将对象 dealloc;
-
- ARC 通过引用计数来追踪对象的生命周期;
-
- Swift 汇编器通过插入 retain/release 操作,来控制引用计数;
-
- 当对象的引用计数为 0 时,Swift runtime 会将对象 dealloc。
- 现有如下示例代码:
class Traveler {
var name: String
var destination: String?
init(name:String) {
self.name = name
}
}
func test() {
let travel1 = Traveler(name: "Kody") // ①
// Retain
let travel2 = travel1 // ②
// Release // ③
travek2.destination = "Big Sur" // ④
// Release
print("Done traveling")
}
- 编译器编译器会在引用开始时插入 retain 操作,以及在最后一次使用时,插入 release 操作,由此可以分析出:
-
- ① Travler 对象初始化时,引用计数为 1;
-
- ② 在 travl2 引用 trvel1 时,对 Travler 对象进行 retain 操作,此时,引用计数为 2;
-
- ③ 在最后一次使用 travel1 时,对 Travler 对象进行 release 操作,此时引用计数为 1;
-
- ④ 在最后一次使用 travel2 时,对 Travler 对象进行 release 操作,此时引用计数为 0。
- 那么,此时 Travler 对象的生命周期从初始化(①)开始,到最后一次使用(④)结束。
- 在开启优化的情况下,运行该函数,结果为:
Traveler deinit ........
Done traveling
- 可以看出,在执行 print(“Done traveling”) 之前,Traveler 已经被释放,这样能够保证对象的最短生命周期。和 C++ 、OC 是不一样的,后者是在右括号执行完成后,才会销毁对象。
- 在大多数情况下,是没有问题的,但是如果存在弱引用(weak)或无主引用(unown)就需要特别注意,来看如下示例:
class Traveler {
var name: String
var account: Account?
init(name:String) {
self.name = name
}
}
class Account {
weak var traveler: Traveler?
var points: Int
init(points: Int, traveler: Traveler?) {
self.traveler = traveler
self.points = points
}
func printSummary() {
if let travel = traveler {
print("\\(travel.name) has \\(points) points")
}
}
}
func test() {
let travel = Traveler(name: "Kody")
let account = Account(points: 1000, traveler: travel)
travel.account = account
account.printSummary()
}
- Travel 对 Account 对象强引用,Account 对 Travel 对象弱引用。可以注意到,由于 account 引用 travel 是弱引用, 在代码 travel.account = account 时,此时,travel 对象已经被释放,当执行 travel.account = account 时,travel 对象为 nil,条件不成立,不会打印分数,此时,将会产生 bug。
- 通过 withExtendedLifetime 可以延长对象的生命周期,防止潜在的错误。如下所示,将 travel 的生命周期延长至 account.printSummary() 执行完:
func test() {
let travel = Traveler(name: "Kody")
let account = Account(points: 1000, traveler: travel)
travel.account = account
withExtendedLifetime(travel, {
account.printSummary()
})
}
- 或者使用 defer,延长至整个函数结束:
func test() {
let travel = Traveler(name: "Kody")
let account = Account(points: 1000, traveler: travel)
defer {withExtendedLifetime(travel, {
})}
travel.account = account
account.printSummary()
}
- 但这种方式不好,这会增加维护成本,也违背减少对象生命周期的初衷。
- 如果可以把向对象的访问限制为只允许强引用,就可以防止对象生命周期意外。在这里,printSummary() 函数被移回到了 Traveler 类并且 Account 类中的弱引用是隐藏的,现在必须通过 Travel 调用 printSummary() 函数,由于在 Traveler 中,对 Account 是强引用,可以消除潜在的错误。如下所示:
class Traveler {
var name: String
var account: Account?
init(name:String) {
self.name = name
}
func printSummary() {
if let account = account {
print("\\(name) has \\( account.points) points")
}
}
}
class Account {
private weak var traveler: Traveler?
var points: Int
init(points: Int, traveler: Traveler?) {
self.traveler = traveler
self.points = points
}
}
func test() {
let travel = Traveler(name: "Kody")
let account = Account(points: 1000, traveler: travel)
travel.account = account
travel.printSummary()
}
- 那么,现在重新设计,避免 weak/unown 引用。增加一个中间类,把必须的信息存储到中间类中,打破原来的弱引用或者 unown 引用,使用中间类打破对象之间的互相引用。重新设计过后,类声明如下:
class PersonalInfo {
var name: String
}
class T raveler {
var info: PersonalInfo
var account: Account?
}
class Account {
var info: PersonalInfo
var points: Int
}
- 它们之间的引用关系如图所示:
- 开启优化之后,会缩短对象的生命周期,如果在工程在对象的 deinit 方法做了依赖外部对象,那么可能此时依赖的外部对象已经释放,从而造成一些逻辑错误,这里需要注意。
以上是关于Swift之深入解析Xcode13对Swift对象生命周期的优化的主要内容,如果未能解决你的问题,请参考以下文章
Swift之深入解析如何使用Xcode和LLDB v2修改UI元素