Swift 使用自动引用计数(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") }
}
class TestViewController: UIViewController {
var john: Persion? = nil
var unit4A: Apartment? = nil
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.red
john = Persion(name: "John")
unit4A = Apartment(unit: "4A")
}
deinit {
print("TestViewController-deinit")
}
}
在两个实例被创建和赋值后,下图表现了强引用的关系。变量 john
现在有一个指向 Person
实例的强引用,而变量 unit4A
有一个指向 Apartment
实例的强引用:
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来解包和访问可选变量 john
和 unit4A
中的实例,这样实例的属性才能被赋值:
class TestViewController: UIViewController {
var john: Persion? = nil
var unit4A: Apartment? = nil
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.red
john = Persion(name: "John")
unit4A = Apartment(unit: "4A")
//做关联
john?.apartemtn = unit4A
unit4A?.tenant = john
}
deinit {
print("TestViewController-deinit")
}
}
在将两个实例联系在一起之后,强引用的关系如图所示:
不幸的是,这两个实例关联后会产生一个循环强引用。Person
实例现在有了一个指向 Apartment
实例的强引用,而 Apartment
实例也有了一个指向 Person
实例的强引用。因此,当你断开 john
和 unit4A
变量所持有的强引用时,引用计数并不会降为 0
,实例也不会被 ARC 销毁:
解决实例之间的循环强引用
Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。
弱引用:前面加上 weak
关键字表明这是一个弱引用。ARC 会在引用的实例被销毁后自动将其弱引用赋值为 nil
。并且因为弱引用需要在运行时允许被赋值为 nil
,所以它们会被定义为可选类型变量,而不是常量。
注意 当 ARC 设置弱引用为 nil
时,属性观察不会被触发。
class Apartment: NSObject {
let unit: String
init(unit: String) {self.unit = unit}
//设置弱引用
weak var tenant:Persion?
deinit { print("Apartment-deinit") }
}
现在,两个关联在一起的实例的引用关系如下图所示:
无主引用 和弱引用类似,前面加上关键字 unowned
表示这是一个无主引用。ARC 无法在实例被销毁后将无主引用设为 nil