【问题标题】:'self' used before all stored properties are initialized'self' 在所有存储的属性被初始化之前使用
【发布时间】:2016-04-01 04:23:07
【问题描述】:

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

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 不变?

【问题讨论】:

    标签: swift initialization swift2


    【解决方案1】:

    在这种情况下,我建议您将属性设为变量,但通过计算属性将其隐藏(使其看起来像常量):

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

    【讨论】:

    • 这是一个很好的答案。它到达了我想要的界面,尽管@matt 的解释很棒
    • swift 是一种奇怪的初始化语言。
    • 这对我不起作用。它修复了编译错误,但是当我将 self 传递给另一个对象的构造函数,然后该对象在 self 上调用一个函数时,该函数会看到一个完全不同的(未初始化的)self。
    • @svenyonson 只有capitalCity在调用City的构造函数时没有被初始化。如果你想在构造函数中使用它,那么你必须在City 构造函数中使用self。如果问题很复杂,请随时在 StackOverflow 上提问。
    【解决方案2】:

    在保持capitalCity 不变的同时,有什么方法可以解决语法错误?

    不是你设置的方式。问题的根源实际上是为了设置capitalCity,您必须创建一个countryself 的城市。 是编译器反对的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)
        }
    }
    

    【讨论】:

    • 内存泄漏怎么办?
    • @user3441734 那就是unowned解决的问题。
    • @matt:您还可以声明capitalCity 前缀private(set) 以防止变量的外部更改。
    • @appzYourLife 当然可以,但这不会改变这样一个事实,即考虑到两个类之间的结构关系,capitalCity 必须是可选的 var 才能满足编译器认为对象正在正确初始化——这就是问题所在。 “有什么方法可以解决语法错误,同时保持 capitalCity 不变?”没有。
    • @matt:是的,我知道它必须是可选的var。我只是提议将您解决方案中的属性声明更改为private(set) var capitalCity: City! = nil。现在事实上,Country 定义之外的一些代码可能会设置 capitalCity = nil,这对于隐式展开的可选属性是危险的。
    【解决方案3】:

    只要做:

    private(set) var capitalCity: City!
    

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

    我了解到您认为 var capitalCity: City! 做作。我会说the selected answer 确实是做作的;它添加了除了解决与语言相关的问题之外没有其他目的的代码行。这样的行是没有意义的,而 meaning 是代码中最重要的

    【讨论】:

    • 这应该是选择的答案
    • 这应该是选择的答案+1
    【解决方案4】:

    最近在解决类似问题时遇到了这个问题,从 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
        }
    }
    

    干杯!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-12
      • 1970-01-01
      相关资源
      最近更新 更多