Swift
自动引用计数
本文详细介绍了 Swift 中的自动引用计数(ARC)机制,包括其工作原理、循环引用问题及其解决方案(弱引用和无主引用),以及闭包中的循环引用。
Swift 使用自动引用计数(ARC)机制来跟踪和管理应用程序的内存。在大多数情况下,这意味着内存管理在 Swift 中是“这就行了”的,你不需要自己考虑内存管理。当实例不再被需要时,ARC 会自动释放类实例所占用的内存。
ARC 的工作原理
每当你创建一个类的新实例时,ARC 会分配一块内存来存储该实例的信息。当实例不再被需要时,ARC 会释放该内存。为了确保实例在仍被使用时不会消失,ARC 会跟踪当前引用每个类实例的属性、常量和变量的数量。只要存在至少一个有效引用,ARC 就不会释放该实例。
类实例之间的循环引用
如果两个类实例互相持有对方的强引用,就会发生循环引用,导致内存无法被释放。
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: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person? = Person(name: "John")
var unit4A: Apartment? = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil
// 此时两个实例都无法被释放,因为它们互相强引用解决循环引用
Swift 提供了两种方式来解决类实例之间的循环引用:弱引用(weak)和无主引用(unowned)。
弱引用 (Weak References)
弱引用不会保持其引用的实例,因此不会阻止 ARC 释放该实例。弱引用必须声明为可选类型的变量,因为引用的实例可能会被释放。
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person? // 使用 weak 打破循环引用
deinit { print("Apartment \(unit) is being deinitialized") }
}无主引用 (Unowned References)
和弱引用类似,无主引用也不会保持其引用的实例。不同的是,无主引用假定其引用的实例始终存在(非可选类型)。
class Customer {
let name: String
var card: CreditCard?
init(name: String) { self.name = name }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // 使用 unowned,因为信用卡必须关联一个客户
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
}闭包的循环引用
当闭包捕获了 self,并且该闭包又被赋值给了 self 的某个属性时,也会发生循环引用。解决方法是使用捕获列表(Capture List)。
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
}
}总结
- Strong:默认引用类型,保持对象存活。
- Weak:不保持对象存活,必须是可选类型,对象释放后自动变为
nil。 - Unowned:不保持对象存活,必须是非可选类型,访问已释放对象会崩溃。
- Capture List:用于打破闭包和类实例之间的循环引用。
在 GitHub 上编辑
上次更新于