从Swift初始化器调用方法

f4t66c6m  于 2023-09-30  发布在  Swift
关注(0)|答案(6)|浏览(114)

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

class MyClass {
    let myProperty: String

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

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

这过于简化了,但我基本上是试图将myProperty的初始化委托给setupMyProperty()方法。这是我经常用来分解类设置的不同部分的模式。
但是当然,在超级初始化器运行之前我不能调用self,在所有属性都设置好之前我不能运行超级初始化器,所以我处于第22条军规中。最重要的是,由于setupMyProperty()不被认为是初始化器,因此无论如何都不能分配myProperty
谁能告诉我如何在Swift中实现这个模式?

sbdsn5lh

sbdsn5lh1#

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

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

来源:Swift文档,Unowned References和Implicitly Unwrapped Optional Properties

wmtdaxz3

wmtdaxz32#

setupMyProperty是否需要访问self?如果没有,你可以用一个类方法来实现:

class MyClass: NSObject {
    let myProperty: String

    init() {
        myProperty = MyClass.setupMyProperty()
        super.init()
    }

    class func setupMyProperty() -> String {
        return "foo"
    }
}
dz6r00yl

dz6r00yl3#

在示例的内存完全初始化之前,你不能使用self(此时你不能再设置常量属性),所以你指定的初始化器必须做的事情的顺序是:
1.初始化该类添加的所有属性
1.调用超类初始化器(如果有超类)
1.现在你可以使用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。
另外,请参阅文档,特别是关于类继承和初始化的部分,我在上面解释了整个调用顺序的内容。

du7egjpx

du7egjpx4#

尝试将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)
    }
}
hmae6n7t

hmae6n7t5#

class MyClass {

    let myProperty: String

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

fzwojiic6#

惊讶地看到最高投票的答案。使用一个 * 隐式展开的可选 * 会带来很大的安全风险。

如果你想重置你的属性,但要保留一个真实的来源,请使用Constants结构体

class MyClass {
    var myProperty = Constants.myProperty
    
    init() {
        reset()
    }
    
    func reset() {
        myProperty = Constants.myProperty
    }
    
    struct Constants {
        static let myProperty = "hello"
    }
}

如果你想在init中隐藏复杂的设置逻辑,可以使用static func

class MyClass {
    var myProperty: String
    
    init() {
        myProperty = MyClass.setUpProperty()
    }
    
    static func setUpProperty() -> String {
        // Complicated set up logic goes here..
        return "hello"
    }
}

如果你想为一个对象使用不同的初始化器,请使用Constants结构体和初始化逻辑

class MyClass {
    var myProperty = Constants.myProperty1
    
    init() {
        if externalState {
            init1()
        } else {
            init2()
        }
    }
    
    func init1() {
        myProperty = Constants.myProperty1
    }
    
    func init2() {
        myProperty = Constants.myProperty2
    }
    
    struct Constants {
        static let myProperty1 = "hello"
        static let myProperty2 = "there"
    }
}

相关问题