【问题标题】:Call methods from Swift initializer从 Swift 初始化程序调用方法
【发布时间】:2014-07-29 22:04:00
【问题描述】:

假设我在 Swift 中有以下类(存在明显问题)

class MyClass {
    let myProperty: String

    init() {
        super.init()
        self.setupMyProperty()
    }

    func setupMyProperty() {
        myProperty = "x"
    }
}

这过于简化了,但我基本上试图将myProperty 的初始化委托给setupMyProperty() 方法。这是我经常用来分解类设置的不同部分的模式。

但是,当然,在超级初始化程序运行之前我不能调用self,并且在设置所有属性之前我不能运行超级初始化程序,所以我遇到了问题 22。其中因为setupMyProperty() 不被视为初始化程序,所以无论如何它都无法分配myProperty

谁能告诉我如何在 Swift 中实现这个模式?

【问题讨论】:

  • 我不知道这是否适合您,但您可以在声明属性时指定一个值。 let myProperty: String = "x"
  • 是的,我在有意义的地方使用它。但是如果这些东西是基于给定初始化器的参数初始化的,初始化器就会变得混乱。我想这表明我的班级有太多的属性。
  • 查看 WWDC 会话 403:中级 Swift,涵盖类初始化约 29 分钟。

标签: swift


【解决方案1】:
class MyClass {

    let myProperty: String

    init() {
        super.init()
        self.myProperty = {
          // do your logic here 
          return "x" 
        }()
    }
}

【讨论】:

    【解决方案2】:

    尝试将 setupMyProperty() 放在一个常量中。然后它在你初始化之前就在那里,你可以从 init() 调用它。您甚至可以按如下方式访问参数:

    class MyClass {
        var myProperty: String
        let setupMyProperty : (String) -> (void) = { 
            param in
            self.myProperty = param
        }
    
        init(x: String) {
        // removed redundant line: super.init()
            self.setupMyProperty(x)
        }
    }
    

    【讨论】:

    • 你的班级不需要 super.init(),因为 'MyClass' 是一个根类。
    • 当然,我不假思索地做到了。现在删除了违规行。
    【解决方案3】:

    setupMyProperty 需要访问自己吗?如果没有,您可以使用类方法来实现:

    class MyClass: NSObject {
        let myProperty: String
    
        init() {
            myProperty = MyClass.setupMyProperty()
            super.init()
        }
    
        class func setupMyProperty() -> String {
            return "foo"
        }
    }
    

    【讨论】:

    • 这里的问题是,虽然它适用于简单的示例,但重点是在子方法中初始化一组属性。
    • @mprivat 你可以从静态函数返回元组列表
    【解决方案4】:

    在您的实例的内存完全初始化之前,您不能使用self(此时您不能再设置常量属性),因此您指定的初始化程序必须执行的操作顺序是:

    1. 初始化该类添加的所有属性
    2. 调用超类初始化器(如果有超类)
    3. 现在可以使用self、调用方法等

    我不确定对于常量属性的情况是否有很好的解决方法。一种替代方法是将属性声明为隐式展开的可选项,因此它最初设置为 nil:

    class MyClass {
        var myProperty: String!
        init() {
            super.init() // only if you actually have a superclass, and make sure this does not use `myProperty`
            self.setupMyProperty()
        }
    
        func setupMyProperty() {
            myProperty = "x"
        }
    }
    

    注意这一点,它失去了一点类型安全性,因为myProperty 现在可以是nil,如果是当你尝试访问它时,它会导致运行时错误。所以只有当你确定它会被你所有指定的初始化器初始化时才这样做,而不是被初始化器链中setupMyProperty()之前调用的任何东西使用(例如,当超类初始化器调用你覆盖的访问myProperty的方法时),并且从不明确设置为 nil。

    另外,参见。 the docs,尤其是关于我上面解释的整个调用顺序内容的类继承和初始化部分。

    【讨论】:

    • private(set) var myProperty: String 将防止外部恶意更改您的财产价值
    【解决方案5】:

    将其声明为隐式展开的可选项

    class MyClass : NSObject {
        var myProperty: String!
    
        init() {
            super.init()
            self.setupMyProperty()
        }
    
        func setupMyProperty() {
            self.myProperty = "x"
        }
    }
    

    “The Swift Programming Language”手册的第 499 页

    【讨论】:

    • 这样做你不会失去一些安全感吗?我想我必须确保在访问该属性之前对其进行初始化,否则会出现运行时错误。对于这种情况,它不能是一个常数。我想这是我见过的最好的方式,只是不完美
    • 那么没有办法让myProperty 保持不变(let)?
    • 您可以使用闭包或函数,请看本章:Setting a Default Property Value with a Closure or Function。这不是一回事,但它似乎是一个不错的选择。在像setupMyProperty() 这样的函数中移动常量的初始化似乎不是一个好主意恕我直言,因为setupMyProperty() 可以被多次调用。使用闭包初始化器可以避免这种情况
    • @Andrei 在回答时这是手册中的官方解决方案
    • 苹果官方有新的解决方案吗?什么是新的、进化的?如果您有更新的信息,您可以提供一个链接,以便我可以更新我的答案。或者,如果您愿意,可以添加您自己的答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 2020-10-30
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    相关资源
    最近更新 更多