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 实例间强引用的几种方案了。