4

I am new to Swift and getting some problem regarding initializers. I have created a Swift file with the following code :

import Foundation

class SuperClass
{
    var a : Int
    init()
    {
        a = 10
        print("In Superclass")
    }
}

class SubClass : SuperClass
{
    override init()
    {
        print("In Subclass")
    }
}

In the above code, init() of SubClass does not contain call to init() of SuperClass i.e. there is no super.init() in SubClass init().

So my question is:

1. Why is it not giving any error if I don't call designated init() of SuperClass

2. If I am creating an object of SubClass i.e. let s = SubClass(), the output is :

In Subclass

In Superclass

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

PGDev
  • 23,751
  • 6
  • 34
  • 88

5 Answers5

3

As far as I understood your question, you're not only wondering why, when and how the initializer gets called automatically, but also complaining about the missing documentation of this behavior.

First of all I agree with you on the lack of documentation - just like you I'm not able to find anything about this behavior and therefore it should be added to the documentation by Apple.

Why super.init() is called:

As per documentation a designated initializer of the superclass has to be called by a designated initializer of its subclass in order to fully initialize all properties.

Rule 1

A designated initializer must call a designated initializer from its immediate superclass.

Your code example above proves it's obviously done implicitly: print("In Superclass") prints to the console, so super.init() is somehow invoked while creating an instance.

When and how super.init() is called:

There are some conditions to be met in order to allow the compiler to call the designated initializer of the superclass implicitly:

  1. The superclass must have only one designated initializer which is then called. Otherwise the compiler had to choose one to delegate to. This single designated initializer could be also the default initializer or an inherited initializer.

    class SuperClass {
        var a: Int
        init() {
            a = 10
        }
        // introduction of a second designated initializer in superclass:
        init(withValue value: Int) {
            a = value
        }
    }
    
    class SubClass: SuperClass {
        // won't compile:
        // "error: super.init isn't called on all paths before returning from initializer"            
        override init() {}
    }
    
  2. The single designated initializer of the superclass mustn't have any parameters. After all the compiler wouldn't know any appropriate parameter to be passed.

    class SuperClass {
        var a: Int
        // declaration of an initializer with parameter:
        init(withValue value: Int) {
            a = value
        }
    }
    
    class SubClass: SuperClass {
        // won't compile:
        // "error: super.init isn't called on all paths before returning from initializer"            
        override init() {}
    }
    
  3. The designated initializer of the subclass mustn't further read or modify (inherited) instance properties of the superclass or call instance methods of the superclass. That's because of Swift's two-phase initialization process with its corresponding safety checks and the fact that the implicit delegation up to the designated initializer of the superclass happens at the end of the init-Statement in the subclass.

    Safety check 2

    A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.“

    Safety check 4

    An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

    class SuperClass {
        var a: Int
        init() {
            a = 10
        }
    }
    
    class SubClass: SuperClass {
        // won't compile:
        // "error: use of 'self' in property access 'a' before super.init initializes self"            
        override init() {
            a = 10 // modifying inherited self.a before phase 1 of initialization completes isn't valid! 
            // implicit delegation to super.init()
        }
    }
    

    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.

    class SuperClass {
        var a: Int
        init() {
            a = 10
        }
    }
    
    class SubClass: SuperClass {
        // introduction of instance property "b"
        var b: Int
        // compiles finely:
        override init() {
            b = 10 // initializing self.b is required before delegation!
            // implicit delegation to super.init()
        }
    }
    

I hope that helps.

Tobi
  • 674
  • 6
  • 12
2

Every class have at least one designated initializer which is responsible for initializing instance variables.

Here is an extract from the doc :

Classes tend to have very few designated initializers, and it is quite common for a class to have only one. Designated initializers are “funnel” points through which initialization takes place, and through which the initialization process continues up the superclass chain.

Every class must have at least one designated initializer. In some cases, this requirement is satisfied by inheriting one or more designated initializers from a superclass, as described in Automatic Initializer Inheritance below.

You can refer to the complete documentation for further details : https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html

A rule of thumb

  1. Create one designated initializer for your class.
  2. Call the designated initializer of the superclass or let the system figure this out for you.
  3. Create zero or more convenience initializers which will call your designated initializer.
Community
  • 1
  • 1
nverinaud
  • 1,270
  • 14
  • 25
  • But the superclass initializers are not inherited if we give our own designated initializers in the subclass. And why is it getting called by default? – PGDev Jun 30 '16 at 12:09
  • 1
    If you give your own designated initializers, you must call a designated initializer of the superclass. If you do not, the compiler will choose one for you because the superclass instance variables must be initialized ! – nverinaud Jun 30 '16 at 12:11
2

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.

matt
  • 515,959
  • 87
  • 875
  • 1,141
1

You are not accessing the super class variable in the subclass hence super.init() is called followed by the subclass's init. But if you were to try using the super class variable in subclass without calling its initialiser then it will result in a compile time error.

Amruta
  • 56
  • 3
  • [Apple documentation][https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-ID203]] says that subclass must call super class designated initializer. Can you support your statement with any proof? – PGDev Jun 30 '16 at 12:05
1

I didn't try this myself, but the Swift Language Guide says:

Initializer Delegation for Class Types

To simplify the relationships between designated and convenience initializers, Swift applies the following three rules for delegation calls between initializers:

Rule 1

A designated initializer must call a designated initializer from its immediate superclass.

Rule 2

A convenience initializer must call another initializer from the same class.

Rule 3

A convenience initializer must ultimately call a designated initializer.

A simple way to remember this is:

Designated initializers must always delegate up.

Convenience initializers must always delegate across.

So, as it is a 'rule' to call super.init(), it might just be done internally, if not implemented explicitly.

Community
  • 1
  • 1
Flo
  • 2,309
  • 20
  • 27
  • 2
    But its nowhere written that it is called internally. – PGDev Jun 30 '16 at 12:03
  • You can also have a look at the following question: https://stackoverflow.com/questions/25257224/does-a-swift-subclass-always-have-to-call-super-init – Flo Jun 30 '16 at 12:17
  • I read the post..but still the answers doesn't give any proof of super.init() being called internally.. – PGDev Jul 01 '16 at 04:59