对于来自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

注意:这是在操场上。

最佳答案

很明显,你是在一个游乐场或是在一个你不让它们超出范围的环境中(例如,如果它们是某个对象的属性,看看当该对象本身被释放时会发生什么)。但是,为了便于说明,请考虑您的代码的这种排列:

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引用的情况下,最终会在“加拿大”和“温哥华”之间出现一个很强的引用循环,而它们并没有被去初始化。
所以,事实上你看到的“渥太华”被非初始化与强烈的参考周期无关。这只是因为“渥太华”被“温哥华”取代了,使得“渥太华”不再有强有力的参照物,而是被非初始化了。
你不应该对在你原来的例子中你的陈述中没有任何证据得出任何结论。你可以在操场上这样做,或者它们可能是某个对象的属性,而这个对象本身还没有被释放。将这些变量放在一个受约束的范围内,就像我在上面使用unowned函数时所做的那样,可以更好地说明这些对象脱离范围时的真实生命周期。它向我们展示了不解决强参考周期的结果。
总之,您确实需要printdeinitfoo(或unowned)引用来打破这个强引用循环。这不仅仅是一个关于这些变量是否可以被设置为weak的问题,还包括它们是否有可能超出范围并被非初始化。

07-24 09:22