在 Swift 中,unowned 和 weak 是什么? 解决了什么问题? 它们的区别是什么? 相关的代码演示和对比

30 min read

在 Swift 中,unowned 和 weak 都是用来解决循环引用问题的。

循环引用指的是两个对象相互持有对方的强引用,导致它们都无法被释放,从而造成内存泄露。例如,当一个对象持有一个闭包,而该闭包又持有该对象时,就会形成循环引用。

为了解决这个问题,Swift 提供了两种解决方案:unowned 和 weak。

unowned 与 weak 都可以解决循环引用问题,但它们之间有一些区别:

  1. unowned 属性必须有值,不能为 nil,而 weak 属性可以为 nil。
  2. unowned 属性在对象释放时不会被自动置为 nil,访问已释放的对象会导致程序崩溃,而 weak 属性在对象释放时会被自动置为 nil。
  3. unowned 属性一旦设定,就不会再变化,而 weak 属性可以在运行时被修改。

下面是一个使用 unowned 和 weak 的示例:

class Person {
    var name: String
    var car: Car?
    lazy var printName: () -> () = {
        [unowned self] in
        print(self.name)
    }
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

class Car {
    var brand: String
    weak var owner: Person?
    init(brand: String) {
        self.brand = brand
        print("\(brand) is being initialized")
    }
    deinit {
        print("\(brand) is being deinitialized")
    }
}

var john: Person?
var benz: Car?

john = Person(name: "John")
benz = Car(brand: "Benz")

john!.car = benz
benz!.owner = john

john!.printName() // John

john = nil
benz = nil

// 输出:
// John is being initialized
// Benz is being initialized
// John
// John is being deinitialized
// Benz is being deinitialized

在这个例子中,Person 对象持有一个 Car 对象,而 Car 对象又持有一个 Person 对象,形成了循环引用。为了解决这个问题,我们使用 unowned 和 weak 来打破循环引用。

在 Person 类中,我们定义了一个 lazy 属性 printName,它是一个闭包,用来打印人名。由于该闭包持有 Person 对象,所以它也会造成循环引用。为了解决这个问题,我们使用了 unowned self,即该闭包对 self 对象使用无主引用。

在 Car 类中,我们定义了一个 weak 属性 owner,用来持有 Person 对象。由于 Car 对象可能比 Person 对象先被释放,所以我们使用 weak 引用,以防止访问已经被释放的 Person 对象。

最后,我们将 john 和 benz 变量设为 nil,以释放它们所持有的对象。输出结果表明,对象被成功地释放了,没有造成内存泄露。