Swift5.1—无主引用

2020-07-08  本文已影响0人  anny_4243

和弱引用类似,无主引用不会牢牢保持住引用的实例。和弱引用不同的是,无主引用在其他实例有相同或者更长的生命周期时使用。你可以在声明属性或者变量时,在前面加上关键字 unowned 表示这是一个无主引用。

无主引用通常都被期望拥有值。不过 ARC 无法在实例被销毁后将无主引用设为 nil,因为非可选类型的变量不允许被赋值为 nil

重点

使用无主引用,你必须确保引用始终指向一个未销毁的实例。

如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。

下面的例子定义了两个类,CustomerCreditCard,模拟了银行客户和客户的信用卡。这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系可能会造成循环强引用。

CustomerCreditCard 之间的关系与前面弱引用例子中 ApartmentPerson 的关系略微不同。在这个数据模型中,一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系,Customer 类有一个可选类型的 card 属性,但是 CreditCard 类有一个非可选类型的 customer 属性。

此外,只能通过将一个 number 值和 customer 实例传递给 CreditCard 构造器的方式来创建 CreditCard 实例。这样可以确保当创建 CreditCard 实例时总是有一个 customer 实例与之关联。

由于信用卡总是关联着一个客户,因此将 customer 属性定义为无主引用,用以避免循环强引用:

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") }
}

注意

CreditCard 类的 number 属性被定义为 UInt64 类型而不是 Int 类型,以确保 number 属性的存储量在 32 位和 64 位系统上都能足够容纳 16 位的卡号。

下面的代码片段定义了一个叫 john 的可选类型 Customer 变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为 nil

var john: Customer?

现在你可以创建 Customer 类的实例,用它初始化 CreditCard 实例,并将新创建的 CreditCard 实例赋值为客户的 card 属性:

john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)

在你关联两个实例后,它们的引用关系如下图所示:

Customer 实例持有对 CreditCard 实例的强引用,而 CreditCard 实例持有对 Customer 实例的无主引用。

由于 customer 的无主引用,当你断开 john 变量持有的强引用时,再也没有指向 Customer 实例的强引用了:

由于再也没有指向 Customer 实例的强引用,该实例被销毁了。其后,再也没有指向 CreditCard 实例的强引用,该实例也随之被销毁了:

john = nil
// 打印“John Appleseed is being deinitialized”
// 打印“Card #1234567890123456 is being deinitialized”

最后的代码展示了在 john 变量被设为 nilCustomer 实例和 CreditCard 实例的析构器都打印出了“销毁”的信息。

注意

上面的例子展示了如何使用安全的无主引用。对于需要禁用运行时的安全检查的情况(例如,出于性能方面的原因),Swift 还提供了不安全的无主引用。与所有不安全的操作一样,你需要负责检查代码以确保其安全性。 你可以通过 unowned(unsafe) 来声明不安全无主引用。如果你试图在实例被销毁后,访问该实例的不安全无主引用,你的程序会尝试访问该实例之前所在的内存地址,这是一个不安全的操作。

上一篇 下一篇

猜你喜欢

热点阅读