简体   繁体   中英

Where to put super.init() when init() also initializes member variable and references "self"

I'm learning Swift, and I now have this code:

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

}

I have seen other questions about this, but I can't imagine that I would have to create a dummy initializer (with zero args) in SpriteComponent and call that like:

var spriteComponent = SpriteComponent()

in order to call super.init() before referencing "self"

Can anyone explain why I have to do this idiotic tap-dancing? There surely must be a better way to do it right? Swift can't be that *&%&/(% right?

You must initialize all non-optional properties declared in your subclass before you can call super.init()

So you have two ways of solving such issues:

  1. Change property type to optional (either SpriteComponent? or SpriteComponent! ).
  2. Initialize every property either when you declare it or in your initializer before calling super.init

In your case first option suits better.

You can find more info about Swift two-phase initialization here: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

This tutorial may also be helpful: ( Adding Properties to Subclasses paragraph): https://www.raywenderlich.com/121603/swift-tutorial-initialization-part-2

All the non-optional properties must be initialised before the object is created. Make your property an optional.

spriteComponent is a non-optional var. Therefore it has to be initialised before calling super.init (explains the 1st mentioned error).

You can not solve this by calling super.init later, because the constructor of SpriteComponent needs a reference to self, which is only available after calling super.init . (explains the second error)

As a solution you can make spriteComponent an unwrapped optional:

var spriteComponent : SpriteComponent!

This instructs the compiler to allow spriteComponent not to be initialised, and gives you the responsibility do do it yourself at a later point in time.

This "Tap-Dancing" has a reason. In swift, class initialization is a two-phase-initializtion :

Phase #1 - All stored properties are given some initial value (nil is also fine) by the class that defined them

Phase #2 - Now each class may change the initial value and use self

Why is that? Safety mainly - knowing in phase #2 that all properties has some values.

Therefore, in your code, you may not need an empty dummy initializer, but turning your sprite component to an optional could be handy :

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM