Swift中的强参考循环

Swift中的强参考循环,swift,Swift,我正在看《Swift编程语言》一书中“无主引用和隐式展开的可选属性”一节中的示例 他们的示例代码是 class Country { let name: String let capitalCity: City! init(name: String, capitalName: String) { self.name = name self.capitalCity = City(name: capitalName, country: self)

我正在看《Swift编程语言》一书中“无主引用和隐式展开的可选属性”一节中的示例

他们的示例代码是

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
    }
}
如果我只想与国家打交道,并且
城市
类型的唯一目的是成为
国家
的首都,这一点就行了。但是如果我想创建一个城市会发生什么呢

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

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).

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

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

var country = Country(name: "USA", capitalName: "Washington DC")
var chicago = City(name:"Chicago", country: country)
chicago.country.name
将修复它,因为我们的
coutry
变量将阻止
USA
取消分配


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

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

var country = Country(name: "USA", capitalName: "Washington DC")
var chicago = City(name:"Chicago", country: country)
chicago.country.name
将修复它,因为我们的
coutry
变量将阻止
USA
取消分配


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

有两种典型的解决方案:

  • 如果您想主要处理城市问题,请颠倒关系,使
    城市
    强烈引用
    国家
    ,并且
    国家
    指向一个无主实例

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

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

解决方案一是向上的情况,解决方案二是侧向的情况

编辑
  • 第三种选择是,让
    国家拥有其所有城市的集合。我认为这在这个简单的例子中是最有意义的,但这意味着
    国家
    需要在其初始化中创建所有城市,或者有一个添加城市的方法
下面是第二个例子。它相当复杂,对于这个简单的例子来说可能太复杂了,但它演示了如何提取一个公共所有者。如果有很多交叉引用,我通常会使用它。(想想关系数据库吧。这些记录彼此并不拥有。)

阶级国家{ let name:String 城市:城市? init(名称:String){ self.name=名称 } } 阶级城市{ let name:String 无主出租国:国家 init(名称:String,国家:country,isCapital:Bool){ self.name=名称 self.country=国家 如果是资本{ country.capitalCity=self } } } 类行星{ 变量国家:[国家]=[] 变量城市:[城市]=[] } 让地球=行星() 地球。国家=[ 国家(名称:“美国”), 国家(名称:“加拿大”), ] 地球。城市=[ 城市(名称:“华盛顿特区”,国家:地球。国家[0],isCapital:true), 城市(名称:“芝加哥”,国家:地球。国家[0],isCapital:false), 城市(名称:“渥太华”,国家:地球。国家[1],isCapital:true), ]
有两种典型的解决方案:

  • 如果您想主要处理城市问题,请颠倒关系,使
    城市
    强烈引用
    国家
    ,并且
    国家
    指向一个无主实例

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

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

解决方案一是向上的情况,解决方案二是侧向的情况

编辑
  • 第三种选择是,让
    国家拥有其所有城市的集合。我认为这在这个简单的例子中是最有意义的,但这意味着
    国家
    需要在其初始化中创建所有城市,或者有一个添加城市的方法
下面是第二个例子。它相当复杂,对于这个简单的例子来说可能太复杂了,但它演示了如何提取一个公共所有者。如果有很多交叉引用,我通常会使用它。(想想关系数据库吧。这些记录彼此并不拥有。)

阶级国家{ let name:String 城市:城市? init(名称:String){ self.name=名称 } } 阶级城市{ let name:String 无主出租国:国家 init(名称:String,国家:country,isCapital:Bool){ self.name=名称 self.country=国家 如果是资本{ country.capitalCity=self } } } 类行星{ 变量国家:[国家]=[] 变量城市:[城市]=[] } 让地球=行星() 地球。国家=[ 国家(名称:“美国”), 国家(名称:“加拿大”), ] 地球。城市=[ 城市(名称:“华盛顿特区”,国家:地球。国家[0],isCapital:true), 城市(名称:“芝加哥”,国家:地球。国家[0],isCapital:false), 城市(名称:“渥太华”,国家:地球。国家[1],isCapital:true), ]
但如果他想用乡村级的init函数创建一个国会大厦,这是行不通的。这将是一场灾难