SWIFT NOTES - INITIALIZATION

Notes about Swift initialization

Syntax:

// non-failable initializer
init() {
    ...
}

// failable initializers
init?() {
    ...
}

init!() {
    ...
}

Notes:

Setting Initial Values for Stored Properties

There are 3 ways a stored property can get an initial value:

  1. Set value in initializer
  2. Set default value in property declaration
  3. Optional stored properties get nil as default value, if given no default value

Notes:

class Foo {
    // DO NOT forget the trailling `()`, otherwise the closure/function itself
    // would be assigned to as the default value.

    let name: String = {
    ...
    }()

    var id: Int = GenerateRandomID(...)
}

Constant stored property initialization

For constant stored property, if you given a default value in definition, then it’s initialization is complete, you CAN NOT modify it’s value later in initializers.

You can define a constant stored property with no default value, and set an initial value latter in the initializers

For class instances, a constant stored property can only be modified during initialization by the class that introduces it. It cannot be modified by a subclass.

Compiler Synthesized Initializers

  1. default initializer for struct & class

    2 prerequisite:

    1. all stored property are given default values
    2. defines no custom initializer
  2. memberwise initializer for struct

    2 prerequisite

    1. defines no custom initializer
    2. defines at least one variable stored property or constant stored property that is not given a default value (so there is a necessity for the initializer)
  3. rawValue initializer for enum with associated value

    it is a failable initializer init?(rawValue: T) {...}

Initializer Delegation

Use self.init(...) to delegate initialization task to other initializers.

Since defining custom initializer suppresses default & memberwise initializer synthesization, you can define custom initializers in extension, then delegate to default or memberwise initializer in the body.

class can define 2 kinds of initializers:

  1. Designated initializers – init(...)
  2. Convenience initializers – convenience init(...)

3 rules for class initializer delegation

  1. A designated initializer must call a designated initializer from its immediate superclass.
  2. A convenience initializer must call another initializer from the same class.
  3. A convenience initializer must ultimately call a designated initializer.

put simply:

  1. Convenience initializer delegation across – self.init(...)
  2. Designated initializer delegation upwards – super.init(...)

2-Phase Initialization Ensured by 4 Safety Checks

  1. In designated initializer, initialize all local stored properties before delegate up
  2. In designated initializer, delegate up before modifying any inherited stored properties
  3. In convenience initializer, delegate across before modifying any property
  4. In any initializers, only after the 1st-phase initialization is complete then it can
    • call any instance methods or subscripts
    • read any instance properties (stored or computed)
    • reference self as an value

Initializers inheritance

Unlike subclasses in Objective-C, Swift subclasses do NOT inherit their superclass initializers by default. Only when certain rules are satisfied can certain initializers be inherited by subclasses.

Do NOT write the override modifier when providing a matching implementation of a superclass convenience initializer (because it can not delegate up to it counterpart in superclass, the implementation is not considered as a overriding)

2 prerequisites for automatic initializer inheritance

  1. If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
  2. If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

Failable Initializer

Use init?() {...} or init!() {...} to define a failable initializer

Use return nil to fail the initialization process though the Swift initializers do not return any value.

When defining failable initializer:

You can use constant implicitly unwrapped optional property to satisfy rule #2 without first assign an valid initial value to it.

Required Initializer

Write required before the definition of initiializer to indicate that every subclass of this class must implement that initiializer

The required keyword implies override in subclasses

You don’t have to provide an explicit implementation if the initiializer can be inherited