'self' 在所有存储属性初始化之前使用

'self' used before all stored properties are initialized

我正在研究 learn-swift playground 并在学习语言时将其升级到 Swift 2.0。以下代码(可能适用于 Swift 的早期版本)现在会生成两个错误:“'self' 在所有存储的属性初始化之前使用”和 "Constant 'self.capitalCity' used before initialized"

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
    }
}

读取 answer to a similar question 我看到我可以将 let capitalCity: City! 更改为 var capitalCity: City! 并且语法错误已解决。

我意识到在这个人为的例子中,一个国家的首都可以改变,所以这很好,但如果有一个值真的是常量的情况......

有什么方法可以在保持 capitalCity 不变的情况下解决语法错误吗?

Is there any way to resolve the syntax error while keeping capitalCity a constant?

不是你设置的方式。问题的根源其实是为了设置capitalCity,你必须创建一个countryself的City。 那个是编译器反对的self的使用:

self.capitalCity = City(name: capitalName, country: self)
                                                    ^^^^

由于您已将 City 的 country 配置为常量,因此您 必须 在初始化 City 时提供此值。这样你就没有出路了;您必须使 capitalCity 成为可选的 var,以便它具有一些 other 合法的初始值,即 nil。您提出的解决方案实际上是这样工作的:

class Country
{
    let name: String
    var capitalCity: City! = nil // implicit or explicit

    init(name: String, capitalName: String)
    {
        self.name = name
        // end of initialization!
        // name is set (to name), and capitalCity is set (to nil)...
        // ... and so the compiler is satisfied;
        // now, we _change_ capitalCity from nil to an actual City,
        // and in doing that, we _are_ allowed to mention `self`
        self.capitalCity = City(name: capitalName, country: self)
    }
}

在这种情况下,我建议您使 属性 成为一个变量,但通过计算 属性:

隐藏它(让它看起来像一个常量)
class Country {
    let name: String

    private var _capitalCity: City!
    var capitalCity: City {
        return _capitalCity
    }

    init(name: String, capitalName: String) {
        self.name = name
        self._capitalCity = City(name: capitalName, country: self)
    }
}

就这样:

private(set) var capitalCity: City!

它为您提供所需的只读 public 界面。

我知道您觉得 var capitalCity: City! 做作。我会说 确实是做作的;它添加的代码行除了解决与语言相关的问题外没有其他目的。这样的行是没有意义的,意义是代码中最重要的地方。

最近在遇到类似问题时遇到了这个问题,从 swift 5.5(或可能更低)开始,还有另一个有趣的选择。如果您将 Capital City 转换为 lazy var,您实际上可以在初始化中使用 self。

class Country
{
    let name: String

    lazy var capitalCity: City = {
        City(name: capitalName, country: self)
    }()

    private let capitalName: String

    init(name: String, capitalName: String)
    {
        self.name = name
        self.capitalName = capitalName
    }
}

class City
{
    let name: String
    unowned let country: Country

    init(name: String, country: Country)
    {
        self.name = name
        self.country = country
    }
}

干杯!