MST

星途 面试题库

面试题:Swift中弱引用和无主引用在ARC机制下的区别与使用场景

详细阐述Swift中弱引用(weak)和无主引用(unowned)在ARC内存管理体系中的不同之处,包括它们的生命周期管理、内存安全特性以及在实际开发中各自适用的场景,并举例说明。
28.8万 热度难度
编程语言Swift

知识考点

AI 面试

面试题答案

一键面试

生命周期管理

  • 弱引用(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对象,在这种情况下CreditCardCustomer的引用可以使用无主引用。
    • 避免不必要的nil检查:如果确定被引用的对象在整个生命周期内都会存在,使用无主引用可以避免每次使用时进行nil检查,提高代码的简洁性和性能。例如在一个单例对象中,其他对象对单例对象的引用可以使用无主引用,因为单例对象在应用程序的整个生命周期内都存在。