Why can't Swift initializers call convenience initializers on their superclass? Why can't Swift initializers call convenience initializers on their superclass? swift swift

Why can't Swift initializers call convenience initializers on their superclass?


This is Rule 1 of the "Initializer Chaining" rules as specified in the Swift Programming Guide, which reads:

Rule 1: Designated initializers must call a designated initializer from their immediate superclass.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

Emphasis mine. Designated initializers cannot call convenience initializers.

There is a diagram that goes along with the rules to demonstrate what initializer "directions" are allowed:

Initializer Chaining


Consider

class A{    var a: Int    var b: Int    init (a: Int, b: Int) {        print("Entering A.init(a,b)")        self.a = a; self.b = b    }    convenience init(a: Int) {        print("Entering A.init(a)")        self.init(a: a, b: 0)    }    convenience init() {        print("Entering A.init()")        self.init(a:0)    }}class B : A{    var c: Int    override init(a: Int, b: Int)    {        print("Entering B.init(a,b)")        self.c = 0; super.init(a: a, b: b)    }}var b = B()

Because all designated initializers of class A are overridden, class B will inherit all convenience initializers of A. So executing this will output

Entering A.init()Entering A.init(a:)Entering B.init(a:,b:)Entering A.init(a:,b:)

Now, if the designated initializer B.init(a:b:) would be allowed to call the base class convenience initializer A.init(a:), this would result in a recursive call to B.init(a:,b:).


It's because you can end up with an infinite recursion. Consider:

class SuperClass {    init() {    }    convenience init(value: Int) {        // calls init() of the current class        // so init() for SubClass if the instance        // is a SubClass        self.init()    }}class SubClass : SuperClass {    override init() {        super.init(value: 10)    }}

and look at:

let a = SubClass()

which will call SubClass.init() which will call SuperClass.init(value:) which will call SubClass.init().

The designated/convenience init rules are designed that a class initialisation will always be correct.