推荐答案
在 Swift 中,解决循环引用(Strong Reference Cycle)问题的主要方法是使用 弱引用(weak) 或 无主引用(unowned)。具体选择取决于对象之间的关系。
使用 weak 关键字
weak 引用不会增加对象的引用计数,因此不会导致循环引用。当被引用的对象被释放时,weak 引用会自动变为 nil。
// www.javascriptcn.com code example
class Person {
var name: String
weak var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
var unit: String
var tenant: Person?
init(unit: String) {
self.unit = unit
}
deinit {
print("Apartment \(unit) is being deinitialized")
}
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil使用 unowned 关键字
unowned 引用也不会增加对象的引用计数,但它假定对象在引用期间永远不会被释放。如果对象被释放后仍然访问 unowned 引用,会导致运行时崩溃。
// www.javascriptcn.com code example
class Customer {
var name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Card #\(number) is being deinitialized")
}
}
var john: Customer?
john = Customer(name: "John")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil本题详细解读
循环引用的产生
循环引用发生在两个或多个对象相互持有强引用时,导致它们的引用计数永远不会降为零,从而无法被释放。例如:
// www.javascriptcn.com code example
class A {
var b: B?
}
class B {
var a: A?
}
var a: A? = A()
var b: B? = B()
a!.b = b
b!.a = a
a = nil
b = nil在上面的代码中,a 和 b 相互持有强引用,即使将 a 和 b 设置为 nil,它们也不会被释放。
解决方案
weak引用:适用于可能为nil的情况。weak引用不会增加引用计数,当对象被释放时,weak引用会自动变为nil。unowned引用:适用于对象在引用期间永远不会为nil的情况。unowned引用也不会增加引用计数,但如果对象被释放后仍然访问unowned引用,会导致运行时崩溃。
选择 weak 还是 unowned
- 如果两个对象的生命周期不相关,或者一个对象可能比另一个对象先被释放,使用
weak。 - 如果两个对象的生命周期相关,并且一个对象的生命周期不会超过另一个对象,使用
unowned。
通过合理使用 weak 和 unowned,可以有效避免循环引用问题,确保内存得到正确释放。