1

对于我添加的带有反初始化器的 swift 编程指南中的以下代码,无论是否使用 unowned 关键字,生成的调试打印输出都是相同的。swift 编程指南说,当引用彼此的类实例的两个属性永远不会为零时,使用无主和隐式展开的选项是一种打破强引用循环的方法。如果这两个属性都不会为零,那与强引用循环有何不同?例如,为什么我们要在这种特殊情况下使用 unowned 关键字,尤其是当调试读数显示无论是否使用 unowned 内存分配都没有区别时?

class Country {
    let name: String
    var capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
    deinit {print("\(name) is being deinitialized")}
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
    deinit {print("\(name) is being deinitialized")}
}

var canada = Country(name: "Canada", capitalName: "Ottawa")
print("\(canada.name)'s capital city is called \(canada.capitalCity.name)")
canada.capitalCity = City(name: "Vancouver", country: canada)

调试读数:

Canada's capital city is called Ottawa
Ottawa is being deinitialized

注意:这是在操场上。

4

1 回答 1

4

您显然是在操场或某些环境中查看此内容,您不会让它们超出范围(例如,如果它们是某个对象的属性,请查看当该对象本身被释放时会发生什么)。但是,出于说明目的,请考虑您的代码的这种排列:

func foo() {
    let canada = Country(name: "Canada", capitalName: "Ottawa")
    print("\(canada.name)'s capital city is called \(canada.capitalCity.name)")
    canada.capitalCity = City(name: "Vancouver", country: canada)
}

foo()

这就像您的示例,但我将这些变量的范围限制在foo函数内,因此我应该能够看到在该范围内创建和销毁的对象的完整生命周期。

unowned参考Country其中将City报告:

加拿大的首都叫渥太华
渥太华正在取消初始化
加拿大正在取消初始化
温哥华正在取消初始化

如果没有unowned(也不weak是),它将报告:

加拿大的首都叫渥太华
渥太华正在取消初始化

请注意,没有任何与“Canada”或“Vancouver”去初始化相关的打印语句。这是因为在没有unowned引用的情况下,您最终会在“加拿大”和“温哥华”之间形成一个强大的引用循环,并且它们不会被取消初始化。

因此,您看到“渥太华”被取消初始化的事实与强引用循环无关。这只是被取消初始化,因为“渥太华”被“温哥华”取代,“渥太华”没有更多的强引用并且它被取消初始化。

而且,您不应就原始示例中的print陈述缺乏任何证据得出任何结论。deinit您可以在操场上完成此操作,或者它们可能是某个对象本身尚未被释放的属性。将这些变量放在一个受约束的范围内,就像我在上面对foo函数所做的那样,更好地说明了这些对象在超出范围时的真实生命周期。它向我们展示了没有解决我们的强参考周期的结果。

最重要的是,您确实需要unowned(或weak)参考CountryinsideCity来打破这个强大的参考循环。这不仅仅是关于这些变量是否可以设置为 的问题nil,还有它们是否有可能超出范围并被取消初始化。

于 2018-01-24T23:34:25.150 回答