【问题标题】:Strong reference cycles in SwiftSwift 中的强引用循环
【发布时间】:2014-06-05 14:25:58
【问题描述】:

我正在查看“The Swift Programming Language”一书的“Unowned References and Implicitly Unwrapped Optional Properties”部分中的示例。

他们的示例代码是

class Country {
    let name: String
    let capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

如果我想专门处理 Country 并且 City 类型的唯一目的是成为 Country 的首都,则此方法有效。但是如果我想创建一个城市会发生什么?

这会创建一个运行时异常,因为没有保留对 CityCountry 的引用,因为它是一个无主变量:

var chicago = City(name:"Chicago", country: Country(name: "USA", capitalName: "Washington DC"))
chicago.country.name // Playground execution failed: error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=EXC_I386_GPFLT).

如何在不创建强引用循环的情况下允许这样的事情?

【问题讨论】:

  • 在一天结束时,您仍然必须使用与 Objective-c 中相同的策略来处理保留周期。 Swift 只是让表达更容易

标签: swift


【解决方案1】:

有两种典型的解决方案:

  • 如果您想主要处理城市,请反转关系,使CityCountry 具有强引用,而Country 指向一个无主实例。

  • 如果您希望将城市和国家作为相互交叉引用的主要对象,请将所有城市和国家放入集合(或拥有它们的其他形式的商店)中,并使两个引用变得弱。这样他们就不会互相拥有,你也没有循环。

避免保留循环的最佳方法是考虑谁拥有每个对象。对象可以相互拥有,但这应该是一个清晰的层次结构(即树)。如果您在层次结构中存在横向和向上的连接,请将它们设为弱或无主。

方案一是向上的情况,方案二是横向的情况。

编辑

  • 第三种选择是让Country 拥有其所有城市的集合。我认为这在这个简单的情况下最有意义,但这意味着 Country 需要在其初始化中创建所有城市,或者有一个添加城市的方法。

这是第二种情况的示例。它非常复杂,对于这个简单的案例来说可能太多了,但它说明了提取一个共同的所有者。如果有很多交叉引用,我通常会使用它。 (想想关系数据库。记录不属于彼此。)

类国家{ 让名称:字符串 弱 var capitalCity: 城市? 初始化(名称:字符串){ self.name = 名称 } } 类城市{ 让名称:字符串 无主让国家:国家 初始化(名称:字符串,国家:国家,isCapital:布尔){ self.name = 名称 self.country = 国家 如果是资本{ country.capitalCity = 自我 } } } 类行星{ var国家:[国家] = [] var 城市:[城市] = [] } 让地球=行星() 地球国家= [ 国家(名称:“美国”), 国家(名称:“加拿大”), ] 地球.城市= [ 城市(名称:“华盛顿特区”,国家:earth.countries[0],isCapital:true), 城市(名称:“芝加哥”,国家:earth.countries[0],isCapital:false), 城市(名称:“渥太华”,国家:earth.countries[1],isCapital:true), ]

【讨论】:

  • 你有第二种情况的代码示例吗?这是否类似于下面@Sulthan 的示例,只是将它们存储在一起而不是单独的变量中?
【解决方案2】:

在您的示例中,没有人拥有 Country 实例。这意味着它会立即被释放(释放)。

var country = Country(name: "USA", capitalName: "Washington DC")
var chicago = City(name:"Chicago", country: country)
chicago.country.name

将修复它,因为我们的 coutry 变量将阻止 USA 解除分配

如果您使用无主引用,则始终必须在其他地方保留强引用。

【讨论】:

  • 但是如果他想使用 Country 类的 init 函数来创建一个首都城市,这是行不通的。它将创建一个保留周期。
  • @68cherries 不会。城市从不保留国家,因此不可能有任何保留周期。
  • 所以想法是你的两个变量基本上会同时超出范围,因此同时被释放?
  • @BradDwyer 是的。一旦你解除分配国家,你也应该让其所有城市被解除分配。通常的做法是,国家实际上拥有所有城市,因此您不必保留对城市的引用,您可以只保留国家/地区引用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
  • 2014-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多