当两个属性都不能为零时,无主在分配内存方面有什么区别?



对于我添加的带有去初始化器的 swift 编程指南中的以下代码,无论是否使用 unown 关键字,生成的调试打印输出都是相同的。 swift 编程指南说,当引用彼此的类实例的两个属性永远不会为零时,使用无主和隐式解包的可选是打破强引用循环的一种方法。 如果这两个属性永远不会为零,那么这与强参考循环有何不同? 例如,为什么我们要在这种特定情况下使用 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参考City中的Country,它将报告:

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

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

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

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

因此,您看到"渥太华"被取消初始化的事实与强参考周期无关。这只是被取消初始化,因为"渥太华"被替换为"温哥华",留下"渥太华"没有更多的强引用,它被取消初始化。

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

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

最新更新