Reputation: 2047
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?
Upvotes: 1
Views: 1273
Reputation: 7741
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:
SpriteComponent?
or SpriteComponent!
).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
Upvotes: 2
Reputation: 550
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")
}
}
Upvotes: 1
Reputation: 1690
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.
Upvotes: 1
Reputation: 4104
All the non-optional properties must be initialised before the object is created. Make your property an optional.
Upvotes: 1