In what order do initialisers and deinitialisers run for a subclass with stored properties, and what does this print?

27.1.1 · L2 · Interview

Predict first

What does this print?

final class Part {
    let n: String
    init(_ n: String) { self.n = n; print("init part", n) }
    deinit { print("deinit part", n) }
}
class Base {
    let a = Part("base-part")
    init() { print("Base init") }
    deinit { print("Base deinit") }
}
class Derived: Base {
    let b = Part("derived-part")
    override init() { super.init(); print("Derived init") }
    deinit { print("Derived deinit") }
}
func make() {
    let d = Derived()
    print("made", type(of: d))
}
make()
print("after make")
  • init part derived-part, init part base-part, Base init, Derived init, made Derived, Derived deinit, Base deinit, deinit part base-part, deinit part derived-part, after make
  • init part base-part, init part derived-part, Base init, Derived init, made Derived, Base deinit, Derived deinit, deinit part derived-part, deinit part base-part, after make
  • Base init, Derived init, init part base-part, init part derived-part, made Derived, Derived deinit, Base deinit, after make

Mental model. Building a house goes foundation first, then each floor above it. Demolition starts at the top floor and works down; you never remove the foundation while the upper floors are still using it.

Initialisation is two-phase and goes up: the subclass first gives every one of its own stored properties a value (here b, which prints “init part derived-part”), then calls super.init() (which sets up the superclass’s properties and runs its body), and only then runs the rest of its own initialiser. That is why the output starts with the derived part, then the base part, then “Base init”, then “Derived init”.

Teardown goes the other way. When the last strong reference goes, the most-derived deinit body runs first, then its superclass’s body, and the stored properties are released afterwards, which is when each Part prints its own deinit. The two guaranteed rules are “subclass deinit before superclass deinit” and “properties are still alive inside your own deinit”; the order in which the properties are released after the bodies (here base-part before derived-part) is what this toolchain does, so do not build logic on it. The object also dies at the end of make(), after its last use, before “after make” (see Q130 for why the compiler may release even earlier).

The subclass’s own properties come first in init, then the superclass; in teardown the subclass body runs before the superclass body, and the object is gone before the next statement after make().

Common traps

  • Assuming a subclass can use self before its own properties are set. Phase one must finish, and super.init() must be called, before self is usable.
  • Relying on the order in which sibling properties are released. Only “subclass deinit, then superclass deinit” is a promise.

They might follow up with

  • Can deinit throw or take arguments? No, and you never call it yourself; the runtime calls it exactly once.
  • What if deinit stores self somewhere? Do not: resurrecting an object from its own deinit is a bug.

Related questions

  • 27.1.2 What do closures print when they capture a loop variable, a shared variable, or a copy in a capture list?
  • 27.1.3 A closure uses [weak self] and the owner is released while the work is running. What is the difference…
  • 3.1.3 When exactly is an object deallocated: at the end of its scope, or after its last use?
  • 3.2.1 What is the difference between strong, weak, unowned and unowned(unsafe)?
  • 3.4.5 How does a weak reference get zeroed when the object dies?

What do closures print when they capture a loop variable, a shared… →