生命周期管理
- 弱引用(weak):
- 不会增加对象的引用计数。当对象的强引用计数变为0,对象被释放时,指向该对象的所有弱引用会自动被设置为
nil
。这意味着弱引用不会阻止对象被释放,即使有弱引用指向对象,只要没有强引用,对象就会被销毁。
- 例如:
class Person {
let name: String
init(name: String) { self.name = name }
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
weak var tenant: Person?
init() {}
}
var john: Person? = Person(name: "John")
var apartment: Apartment? = Apartment()
apartment?.tenant = john
john = nil // 此时Person对象的强引用计数为0,对象被释放
print(apartment?.tenant) // 输出 nil,因为tenant是弱引用,对象释放后自动变为nil
- 无主引用(unowned):
- 同样不会增加对象的引用计数。与弱引用不同的是,当被引用的对象释放时,无主引用不会被设置为
nil
。这就要求在使用无主引用时,必须确保被引用的对象在无主引用的生命周期内始终存在,否则会导致运行时错误(野指针访问)。
- 例如:
class Customer {
let 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? = Customer(name: "John")
john?.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil // 此时会产生运行时错误,因为CreditCard的customer是无主引用,对象释放后没有变为nil,在deinit时访问已释放的对象
内存安全特性
- 弱引用(weak):
- 由于弱引用会在对象释放时自动变为
nil
,所以在使用弱引用前需要进行nil
检查,这使得代码更加安全,适用于可能会提前释放对象的场景。例如在视图控制器之间的父子关系中,子视图控制器可能会在父视图控制器之前被释放,使用弱引用可以避免悬空引用。
- 无主引用(unowned):
- 无主引用不会自动变为
nil
,如果在对象释放后访问无主引用,会导致运行时错误,所以使用无主引用需要更加小心,确保对象的生命周期足够长。但是无主引用在使用时不需要进行nil
检查,代码更加简洁,在确定对象不会提前释放的场景下可以提高效率。
实际开发适用场景
- 弱引用(weak):
- 视图控制器之间的关系:在父视图控制器持有子视图控制器的情况下,为了避免循环引用,子视图控制器对父视图控制器的引用通常使用弱引用。比如一个
UITableViewController
作为子视图控制器,它可能需要引用父视图控制器来执行某些操作,使用弱引用可以确保当子视图控制器被释放时,不会阻止父视图控制器的释放。
- 代理模式:在代理设计模式中,代理对象对被代理对象的引用通常使用弱引用。例如一个视图对象可能有一个代理对象来处理某些事件,视图对象对代理对象的引用使用弱引用可以避免循环引用,因为代理对象可能在视图对象之前被释放。
- 无主引用(unowned):
- 相互依赖但生命周期已知:当两个对象相互依赖,但其中一个对象的生命周期完全由另一个对象控制时,适合使用无主引用。比如一个
Customer
对象和它的CreditCard
对象,CreditCard
对象的生命周期依赖于Customer
对象,在这种情况下CreditCard
对Customer
的引用可以使用无主引用。
- 避免不必要的
nil
检查:如果确定被引用的对象在整个生命周期内都会存在,使用无主引用可以避免每次使用时进行nil
检查,提高代码的简洁性和性能。例如在一个单例对象中,其他对象对单例对象的引用可以使用无主引用,因为单例对象在应用程序的整个生命周期内都存在。