Why Doesn't Swift Force My Designated Initializer to Call Super

Why doesn't Swift force my designated initializer to call super?

It's not an answer of the why but adding a symbolic breakpoint on [NSObject init] shows that it gets called even if super.init() is commented out.

It definitely seems a special case as replacing NSObject with any other class makes the compiler warn again about the missing super call. My guess is the the compiler handles this as a special case given that it's our base class with just one known designated initializer.

Why is the superclass designated initializer getting called by default?

Why the init() of SuperClass is getting called? Does a subclass init() calls the superclass init() by default?

Basically, yes.

If all the rules say that you should say super.init() and you don't say it, it is called for you.

I don't like this behavior; it is poorly documented, and besides, secretly doing stuff for you seems against the spirit of Swift. But I filed a bug against it long ago and was told it was intended behavior.

Does a swift subclass *always* have to call super.init()

No, you don't have to.

Assume you have the following classes.

class a {
let name: String

init() {
name = "james"
}
}

class b: a {
let title: String

override init() {
title = "supervisor"
}
}

If you instantiate a variable with

let myVar = b()

Then,

  • override init() in b will be called
  • then the init() in a will be called

Even though you didn't explicitly call super.init().


This has been confirmed by Chris Laettner on the swift-user's email list.
It kicks in when your super class has a single designated initializer with a zero-argument init. This is why you don’t have to call super.init() when deriving from NSObject.

*Thanks to Wingzero's comment below

Calling super.init() in initializer of NSObject subclass in Swift

Even though I can't find a place in the documentation where this is described, what happens is that the default superclass initialiser is called at the end of the subclass initialiser if that is the only initialiser of the superclass, and it wasn't called explicitly.

NSObject only has the default initialiser (init()); you can see that the superclass initialiser is called at the end of the subclass initialiser by attempting to reference self (eg. println(self)) in a constructor that does not call super.init(): You are not allowed to do it because the class is not fully initialised at that point.

If you want to use self somewhere in the constructor, the object needs to be fully constructed at that point, so you need to call super.init() manually before then.

Error in Swift class: Property not initialized at super.init call

Quote from The Swift Programming Language, which answers your question:

“Swift’s compiler performs four helpful safety-checks to make sure
that two-phase initialization is completed without error:”

Safety check 1 “A designated initializer must ensure that all of the
“properties introduced by its class are initialized before it
delegates up to a superclass initializer.”

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11

Property 'self.*' not initialized at super.init call

You have to initialize all property before you call super.init in any init method

So,change this before you call super.init()

originView = sourceView //error here

Exception:

  1. optional property
  2. property with default value
  3. lazy property

Swift initializer inheritance

The compiler can synthesise a call to super.init() in a designated initialiser if you don't make the call yourself. So in your case the compiler effectively transforms your code to:

class Superclass {
var a: Int

init() {
self.a = 1
}
}

class Subclass : Superclass {
var b: Int

override init() {
self.b = 2
super.init() // synthesised by the compiler
}
}

This also applies to cases where you're not overriding the superclass' init():

class Subclass : Superclass {
var b: Int

init(b: Int) {
self.b = b
super.init() // also synthesised by the compiler (try removing)
}
}

Note that this synthesis comes with some restrictions, it only applies when:

  1. The superclass has one and only one designated initialiser
  2. This designated initialiser doesn't have any parameters, i.e init()

In all other cases you need to call super.init yourself.

Adding Convenience Initializers in Swift Subclass

My understanding of Initializer Inheritance is the same as yours, and I think we are both well aligned with what the book states. I don't think it's an interpretation issue or a misunderstanding of the stated rules. That said, I don't think you're doing anything wrong.

I tested the following in a Playground and it works as expected:

class RectShape: NSObject {
var size = CGSize(width: 0, height: 0)
convenience init(rectOfSize size: CGSize) {
self.init()
self.size = size
}
}

class SquareShape: RectShape {
convenience init(squareOfSize size: CGFloat) {
self.init(rectOfSize: CGSize(width: size, height: size))
}
}

RectShape inherits from NSObject and doesn't define any designated initializers. Thus, as per Rule 1, it inherits all of NSObject's designated initializers. The convenience initializer I provided in the implementation correctly delegates to a designated initializer, prior to doing the setup for the intance.

SquareShape inherits from RectShape, doesn't provide a designated initializer and, again, as per Rule 1, inherits all of SquareShape's designated initializers. As per Rule 2, it also inherits the convenience initializer defined in RectShape. Finally, the convenience initializer defined in SquareShape properly delegates across to the inherited convenience initializer, which in turn delegates to the inherited designated initializer.

So, given the fact you're doing nothing wrong and that my example works as expected, I am extrapolating the following hypothesis:

Since SKShapeNode is written in Objective-C, the rule which states that "every convenience initializer must call another initializer from the same class" is not enforced by the language. So, maybe the convenience initializer for SKShapeNode doesn't actually call a designated initializer. Hence, even though the subclass MyShapeNode inherits the convenience initializers as expected, they don't properly delegate to the inherited designated initializer.

But, again, it's only a hypothesis. All I can confirm is that the mechanics works as expected on the two classes I created myself.

How to call designated superclass initializers in Swift

Like this

init(frame: CGRect) {
super.init(frame: frame)
// Initialization code
}

Decodable forces super open class to implement the initializer

Decodable forces super open class to implement the initializer

If you don't inherit the required initializer the superclass has then you have to implement it yourself.

Required Initializers

Write the required modifier before the definition of a class initializer to indicate that every subclass of the class must implement that initializer: <..>

You must also write the required modifier before every subclass implementation of a required initializer, to indicate that the initializer requirement applies to further subclasses in the chain.

You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.

How you can avoid implementing it yourself:

Initializer Inheritance

Rule 1

If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.

Sources

  • Initialization #Required Initializers
  • Initialization #Automatic Initializer Inheritance


Related Topics



Leave a reply



Submit