Launch your Swift skills to the next level as you continue your study of initialization to cover class initialization, subclasses, and convenience initializers.
First, congratulations for a great article. My knowledge about initializers has improved atronomically.
Nevertheless, I have a question that is bothering me for a long time. Working with SpriteKit, I wanted to create my own Scene class, derived from SKScene. My DerivedScene has a property I want to initialize inside a convenience initializer that takes an .sks filename and the property value. So I wrote this:
class DerivedScene: SKScene {
var localData: String
override init(size: CGSize) {
localData = ""
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init?(fileNamed: String, localData: String) {
self.init(fileNamed: fileNamed) // Compiler error!
self.localData = localData
}
}
No matter what I try, I have found no way to use this convenience initializer init?(fileNamed:). Seems to me that, as this initializer is from SKNode, and that SKScene does not implement it, I will never be able to use it.
Thanks Ray! I do, I use the closure initialization pattern all the time, I’m a big fan. I like it because it removes a lot of code from the initializer that is typically not interesting like setting properties and whatnot and places the initialization code at the declaration site. Overall this pattern makes code a lot easier to read. I think lazy makes sense when the initialization is expensive and might not be necessary. It sometimes feels like premature optimization to be honest though. My two cents.
Yeah, this is exactly where things get unfortunately unclear. Try this bit of code:
class DerivedScene: SKScene {
var localData: String = ""
override init(size: CGSize) {
localData = ""
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init?(fileNamed: String) {
self.init(fileNamed: fileNamed)
}
convenience init?(fileNamed: String, localData: String) {
self.init(fileNamed: fileNamed)
self.localData = localData
}
}
It looks like the compiler cannot see the convenience initializer unless we re-declare it in our Swift class. The code that you posted should work according to all the rules. This is probably a bridging issue between Objective-C and Swift. Because Objective-C does not play by the same initialization rules, sometimes I’ve seen some bizarre behavior like in this case.
That is really useful tutorial.
Just one thing, it seems to me that one of the example initializers is not what it should be, more specifically this one:
No reason to set default value of encasingMaterial, since we have it as parameter and liquidType is not initialized. It’s really interesting that the compiler doesn’t complain. If you try to instantiate an object with that particular initializer though…
I think you meant it to be like this:
Yes I think this is an error. What happens when you call this is an infinite loop of the Init #3e calling itself. you can test it by adding a print (“Init #3E”) in the initializer then create an instance using that initializer
Thanks, this was helpful. I’m new to Swift, having some trouble adjusting to some of the new concepts. What if I have an object with a function for resetting state, like this:
class BeanCounter {
var pintoBeansCount: Int
var blackBeansCount: Int
init() { pintoBeansCount = 0; blackBeansCount = 0 }
func reset() -> Void { pintoBeansCount = 0; blackBeansCount = 0 }
}
How do people implement state-reset functions without duplicating the code in their initializers? Or is it considered un-Swifty to have an object that allows itself to be reset?
“There is one downside to overriding superclass designated initializers with subclass designated initializers. If the subclass designated initializer has logic, you can’t delegate to it from a convenience initializer.”
I don’t quite follow that part. In a playground, I made a class A with a designated initializer, then I made a class B with a designated initializer overriding the designated initializer in A. B’s designated initializer has if-else logic**. But my convenience initializer in B managed to delegate to B’s designated initializer just fine.
My code below:
var str = “Hello, playground”
class A {
let a: Int
// designated
init(a: Int) {
self.a = a
}
}
class B : A {
let b: Int
// designated
override init(a: Int) {
if (a > 3) {
b = 5
}
else {
b = 4
}
super.init(a: a)
}