需要帮助理解在Swift中方便初始化器和实例变量解包之间的交互

时间:2017-12-16 18:55:47

标签: swift sprite-kit initialization skshapenode forced-unwrapping

我正在倾听关于初始化程序和解包的Swift文档,而且我没有找到关于它的工作方式的基本信息。

目标:我想继承SKShapeNode并使用let reverse : Bool来定义一个只在构造函数中设置的实例变量,永远不需要修改。这似乎是定义这些变量的正确方法,但这在下面的代码中会爆炸。

以下代码正常工作,即它会编译,不会崩溃并让我获得所需的行为。

public class BeamedNotesNode: SKShapeNode {

    var notes : [Note]!
    var noteNodes : [NoteNode]?
    var beam : BeamNode?
    var childBeams : [BeamNode]?
    var reverse : Bool?

    convenience public init(
        withTicks notes: [Note],
        reverse: Bool = false)
    {
        self.init(rect: CGRect(x: 0, y: 0, width: 1, height: 1))

        self.notes = notes
        self.reverse = reverse
        ...

但是,当我修改它以使用let reverse : Bool时,我在编译时收到错误:Class 'BeamedNotesNode' has no initializers

public class BeamedNotesNode: SKShapeNode {

    var notes : [Note]!
    var noteNodes : [NoteNode]?
    var beam : BeamNode?
    var childBeams : [BeamNode]?
    let reverse : Bool

    convenience public init(
        withTicks notes: [Note],
        reverse: Bool = false)
    {
        self.init(rect: CGRect(x: 0, y: 0, width: 1, height: 1))

        self.notes = notes
        self.reverse = reverse
        ...

似乎我应该能够使用let并避免使用丑陋的强制解包实例变量。我如何在这里实现这一目标?

2 个答案:

答案 0 :(得分:1)

问题是,如果你有一个非可选常量,你需要确保它由指定的初始化器初始化。

如果您希望它的行为类似于非可选常量,那么最简单的解决方案是(a)为变量提供默认值;但(b)制作二传private,即

private(set) var reverse = false

我知道这不是你想要的,但外部它的行为就像一个常量,但你的便利初始化程序仍然可以用作为参数提供的任何东西覆盖该值。

答案 1 :(得分:0)

这应该有效

public class BeamedNotesNode: SKShapeNode {

    var notes : [Note]!
    var noteNodes : [NoteNode]?
    var beam : BeamNode?
    var childBeams : [BeamNode]?
    let reverse : Bool

    public init(withTicks notes: [Note], reverse: Bool = false) {
        self.notes = notes
        self.reverse = reverse
        super.init()
    }

    public required init?(coder aDecoder: NSCoder) {
        self.reverse = false
        super.init(coder: aDecoder)
    }

}

发生了什么事?

这里涉及初始化器的一些规则。

事实#1

当您向BeamedNotesNode添加常量属性时,编译器希望您提供指定的初始化程序(不是方便的)来填充该常量属性。

事实#2

但是,由于您添加了指定的初始化程序,因此超类(SKShapeNode)中指定的初始化程序不再在您的类之外可用。 这使您的班级不再符合NSCoding,因此您需要手动添加其他初始化程序

public required init?(coder aDecoder: NSCoder) {
    self.reverse = false
    super.init(coder: aDecoder)
}

现在编译器很高兴。

考虑

我强烈建议您避免在代码中使用强制解包值,除非您没有其他选择。

所以这个

var notes : [Note]!

应该成为

var notes : [Note]?