当 init() 还初始化成员变量并引用“self”时,将 super.init() 放在哪里

Posted

技术标签:

【中文标题】当 init() 还初始化成员变量并引用“self”时,将 super.init() 放在哪里【英文标题】:Where to put super.init() when init() also initializes member variable and references "self" 【发布时间】:2016-10-05 07:34:18 【问题描述】:

我正在学习 Swift,现在我有了这个代码:

import SpriteKit
import GameplayKit

class Player: GKEntity 

var spriteComponent : SpriteComponent

init(imageName: String) 
    super.init() // gives error: self.spriteComponent not initialized at super.init call
    let texture = SKTexture(imageNamed:  imageName)
    spriteComponent = SpriteComponent(entity: self, texture: texture, size:  texture.size())

    // super.init() Placing it here gives error on line above: "self used before super.init() call"
    addComponent(spriteComponent)


required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")



我已经看到了关于这个的其他问题,但我无法想象我必须在 SpriteComponent 中创建一个虚拟初始化程序(零参数)并调用它:

var spriteComponent = SpriteComponent()

为了在引用“self”之前调用 super.init()

谁能解释我为什么要跳这种愚蠢的踢踏舞?肯定有更好的方法来做到这一点吗? Swift 不可能是 *&%&/(% 对吗?

【问题讨论】:

尝试改用var spriteComponent : SpriteComponent! 好的,那么似乎有解决方案。来自 Java/C# 等,如果没有另外指定,我习惯于初始化为 null 的变量。我可以忍受将其初始化为零。我仍然对所有这些都不满意?和 ! 在 Swift 中.. @PeterAnderson !和 ?一旦你掌握了符号,它就非常简单,所以如果一个变量没有任何变量,那意味着它根本不能为 nil,所以基本上它不能有一个空值开始。 ! 意味着一开始可以是空的,但是一旦分配它就不能再为 nil(如果你尝试在它最初仍然为 nil 时访问它,应用程序将崩溃),? 基本上就像一个普通的 java变量可以存放任何东西,但是在您可以访问它之前,您必须始终“解包”它,这基本上意味着在使用它之前确保它不为零,例如if let x = OptionalVariable 好的谢谢解释。但是访问后来在init() 中构造的成员var node : EntityNode! 仍然给我"Value of optional type 'EntityNode?" not unwrapped;..." 【参考方案1】:

您必须先初始化子类中声明的所有非可选属性,然后才能调用 super.init()

所以你有两种方法可以解决这些问题:

    将属性类型更改为可选(SpriteComponent?SpriteComponent!)。 在声明每个属性时或在调用 super.init 之前在初始化程序中初始化每个属性

在您的情况下,第一个选项更适合。

您可以在此处找到有关 Swift 两阶段初始化的更多信息: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

本教程也可能会有所帮助:(向子类添加属性段落): https://www.raywenderlich.com/121603/swift-tutorial-initialization-part-2

【讨论】:

【参考方案2】:

必须在创建对象之前初始化所有非可选属性。将您的属性设为可选。

【讨论】:

【参考方案3】:

spriteComponent 是一个非可选变量。因此必须在调用super.init 之前对其进行初始化(解释第一个提到的错误)。

你不能通过稍后调用super.init来解决这个问题,因为SpriteComponent的构造函数需要对self的引用,只有调用super.init后才能使用。 (解释第二个错误)

作为一种解决方案,您可以将 spriteComponent 设为未包装的可选:

var spriteComponent : SpriteComponent!

这指示编译器允许spriteComponent 不被初始化,并让您有责任在以后自己做。

【讨论】:

【参考方案4】:

这种“踢踏舞”是有原因的。 在swift中,类初始化是一个两阶段的初始化:

第 1 阶段 - 所有存储的属性都由定义它们的类赋予一些初始值(nil 也可以)

第 2 阶段 - 现在每个类都可以更改初始值并使用 self

这是为什么呢?安全为主 - 在第 2 阶段了解所有属性都有一些价值。

因此,在您的代码中,您可能不需要一个空的虚拟初始化程序,但将您的 sprite 组件转换为可选项可能会很方便:

class Player: GKEntity
var spriteComponent : SpriteComponent? = nil  // optional

init(imageName: String)

    super.init() // now OK

    let texture = SKTexture(imageNamed:  imageName)
    spriteComponent = SpriteComponent(entity: self, texture: texture, size:  texture.size())!
    addComponent(spriteComponent!)  // unwrap here

required init?(coder aDecoder: NSCoder)

    fatalError("init(coder:) has not been implemented")

【讨论】:

以上是关于当 init() 还初始化成员变量并引用“self”时,将 super.init() 放在哪里的主要内容,如果未能解决你的问题,请参考以下文章

python中__init__()括号里面的变量应该如何写,分别表示啥?

刨根问底:对于 self = [super init] 的思考

python 面向程序编程

python中类变量,成员变量

Python的类成员变量默认初始值的坑

python-类的基本使用