前言
Swift 提供了两种方法来解决使用类的属性时的循环强引用的问题,他们是 弱引用(weak reference)和无主引用(unowned reference)。
简单的说,当其中一个实例有更短的生命周期的时候,使用弱引用。 而当一个实例有相同或者更长的生命周期的时候,使用无主引用
一:弱引用
弱引用不会保持所引用的实例,所以即使引用存在,实例也可以被销毁。 因此 ARC 在引用的实例被销毁后,会自动为他赋值 nil 。因为弱引用允许他们的值在程序允许时被赋值为 nil,所以他们都被定义为可选类型。
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) 被销毁了") }
}
class Apartment {
let name: String
init(name: String) { self.name = name }
weak var person : Person?
deinit {
print("\(name) 被销毁了")
}
}
var yunfei : Person? = Person(name: "云飞")
var zhalongkou : Apartment = Apartment(name: "闸弄口")
yunfei?.apartment = zhalongkou
zhalongkou?.person = yunfei
yunfei = nil
zhalongkou = nil
yunfei 持有对 zhalongkou的强引用,zhalongkou 持有对 yunfei 的弱引用。
这个时候断开 yunfei 所保持的强引用,Person 类就没有强引用指向它了。所以它就销毁了
二:无主引用
无主引用也不会一直保持引用的实例,但是无主引用用来其他实例有相同或者更长的生命周期。 (无主引用应该一直指向一个未销毁的实例)
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) 被销毁了") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("信用卡 #\(number) 被销毁了") }
}
var yunfei : Customer? = Customer(name: "云飞")
yunfei?.card = CreditCard(number: 13141516, customer: yunfei!)
yunfei = nil
yunfei = nil
Customer 和 CreditCard 之间的关系与前面弱引用例子中 Apartment 和 Person 的关系略微不同。在这个数据模型 中,一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系, Customer 类有 一个可选类型的 card 属性,但是 CreditCard 类有一个非可选类型的 customer 属性。
现在 yunfei 持有对 Customer类型的强引用。ICBC_card 持有对Customer 的无主引用。这个时候把 yunfei 置空,没有指向 Customer 的强引用了。所以该实例被销毁了,同时没有指CreditCard的强引用,所以它的实例也被销毁了
三:隐式解析可选属性
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
}
}
Country 的构造函数调用了 City 的构造函数。然而,只有 Country 的实例完全初始化后, Country 的构造函数 才能把 self 传给 City 的构造函数。
为了满足这种需求,通过在类型结尾处加上感叹号City!的方式,将Country 的 capitalCity 属性声明为隐 式解析可选类型的属性。这意味着像其他可选类型一样,capitalCity属性的默认值为 nil,但是不需要展开它 的值就能访问它。
由于 capitalCity默认值为 nil,一旦Country的实例在构造函数中给 name 属性赋值后,整个初始化过程就完 成了。这意味着一旦name 属性被赋值后, Country 的构造函数就能引用并传递隐式的self 。 Country 的构造函 数在赋值 capitalCity 时,就能将 self作为参数传递给 City 的构造函数。
以上的意义在于你可以通过一条语句同时创建 Country 和 City 的实例,而不产生循环强引用,并且 的属性能被直接访问,而不需要通过感叹号来展开它的可选值。
以上就是解决 Swift 实例间强引用的几种方案了。