Initialization and Deinitialization

This section documents the Swift 6 language mode as shipped by Swift 6.3, as published in The Swift Programming Language at docs.swift.org, which is the reference these pages are written and verified against. 6.4-beta-only features are always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against docs.swift.org before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

Initialization is the process of preparing an instance of a class, structure, or enumeration for use: setting an initial value for every stored property and running any other setup needed before the instance is used. Deinitialization is its counterpart on classes only, running just before an instance is deallocated.

Setting Initial Values, Initializer Parameters and Labels

struct Celsius {
    var temperatureInCelsius: Double

    init() {
        temperatureInCelsius = 0        // every stored property must have a value by the time init returns
    }

    init(fromFahrenheit fahrenheit: Double) {          // an argument label distinct from the parameter name
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }

    init(_ celsius: Double) {                           // `_` suppresses the argument label entirely
        temperatureInCelsius = celsius
    }
}

let boilingPoint = Celsius(fromFahrenheit: 212.0)
let freezingPoint = Celsius(0.0)

An init declaration looks like a method with no func keyword and no return type; like any function or method, each parameter can have both an argument label and a distinct parameter name, or _ to suppress the label at the call site. Every stored property that isn’t given a default value in its declaration must be set inside every initializer, in some order, before the initializer returns (or, for a class, before it delegates up to its superclass — see Two-Phase Initialization below).

Optional Properties and Constants During Initialization

class SurveyQuestion {
    let text: String                  // a `let` property: may be set during init, never again afterward
    var response: String?             // an Optional property needs no explicit initial value: it defaults to nil

    init(text: String) {
        self.text = text
    }

    func ask() -> String { text }
}

let question = SurveyQuestion(text: "Do you like Swift?")
print(question.ask())
question.response = "Yes, I do like Swift!"     // fine even though `text` is a `let` -- only `response` is mutable

A property typed as an Optional automatically starts as nil and needs no explicit initial value, since nil is a valid, fully-initialized value for it. A let property may be assigned exactly once during initialization — by the initializer that first sets it — and is fixed for the rest of the instance’s lifetime afterward (a subclass initializer may not modify a let property it inherited, even before super.init returns, unless it is the class that originally declared it).

Default and Memberwise Initializers; Initializer Delegation for Value Types

struct Size { var width = 0.0, height = 0.0 }             // both properties have defaults
let defaultSize = Size()                                   // the synthesized default initializer
let sized = Size(width: 2.0, height: 2.0)                   // the synthesized memberwise initializer

struct Point { var x = 0.0, y = 0.0 }

struct Rect {
    var origin = Point()
    var size = Size()

    init() {}                                               // uses the property defaults above

    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }

    init(center: Point, size: Size) {
        let originX = center.x - size.width / 2
        let originY = center.y - size.height / 2
        self.init(origin: Point(x: originX, y: originY), size: size)   // delegates sideways to another init on Self
    }
}

A structure or enumeration with no custom initializers of its own receives a compiler-synthesized default initializer whenever every stored property has a default value; a structure additionally receives a memberwise initializer, with one labeled parameter per stored property, whenever it declares no initializer at all (see Structures and Classes). Once any custom initializer is written, as Rect shows, the synthesized ones are no longer generated automatically — but one value-type initializer may call self.init(…​) to delegate to another initializer on the very same type, avoiding repeating setup logic.

Designated vs. Convenience Initializers

A three-level class hierarchy (Base, Mid, Leaf) showing each class’s designated initializer delegating up to its superclass’s designated initializer and a convenience initializer delegating across to a designated initializer on the same class, split into Phase 1 (every stored property set, bottom to top) and Phase 2 (further customization available, top to bottom)
class Food {
    var name: String

    init(name: String) {              // the designated initializer: fully sets up this class's own storage
        self.name = name
    }

    convenience init() {               // a convenience initializer: must delegate across, never straight to super
        self.init(name: "[Unnamed]")
    }
}

class RecipeIngredient: Food {
    var quantity: Int

    init(name: String, quantity: Int) {          // designated: sets this class's storage, then delegates UP
        self.quantity = quantity
        super.init(name: name)
    }

    override convenience init(name: String) {     // convenience, overriding an inherited convenience initializer
        self.init(name: name, quantity: 1)         // delegates ACROSS to this class's own designated initializer
    }
}

let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

A designated initializer is a class’s primary initializer: it sets every property that class introduces and then calls a designated initializer on its immediate superclass (or is the class’s only initializer, if it’s a base class). A convenience initializer, marked convenience, is a secondary, supporting initializer that must ultimately delegate across to a designated initializer on the same class, rather than delegating up to a superclass directly — convenience initializers exist to provide shortcuts through a designated initializer’s full parameter list, not a second, independent path to fully initializing an instance.

Two-Phase Initialization and Safety Checks

Every class initializer performs its work in two phases. Phase 1 runs bottom-up: each designated initializer, starting with the one actually called, sets its own class’s stored properties, then delegates up to its superclass’s designated initializer, which does the same, all the way to the base class — only once the base class’s own properties are set does phase 1 complete and every stored property in the whole hierarchy have an initial value. Phase 2 then runs top-down: each initializer, now able to safely use self (call methods, read and write properties, refer to self as a value), gets a chance to further customize the instance before it’s handed back to the caller, from the base class’s initializer down to the one the caller actually invoked.

The compiler enforces four safety checks that make this ordering sound: a designated initializer must set all of its class’s own properties before delegating up; a designated initializer must delegate up before assigning a value to an inherited property (assigning to an inherited property first, then delegating, would let the superclass initializer overwrite it); a convenience initializer must delegate across before assigning to any property, inherited or its own; and no initializer may call an instance method, read an instance property, or refer to self as a value until after phase 1 has completed for the whole hierarchy.

Initializer Inheritance, Overriding, and required

class SomeClass {
    required init() {}     // every subclass must provide its own (possibly inherited) implementation of this
}

class SomeSubclass: SomeClass {
    required init() {}      // `required` is repeated (without `override`) to keep the requirement propagating
}

Unlike methods, a subclass does not inherit its superclass’s initializers by default — this prevents a subclass that adds new, non-optional stored properties from being reachable through an inherited initializer that never sets them. A subclass automatically does inherit all of its superclass’s designated initializers if it overrides none of them itself, and automatically inherits all of the superclass’s convenience initializers if it provides overrides for every one of the superclass’s designated initializers (matching the same override rules as any other member, using override). Marking a designated initializer required forces every subclass, direct or indirect, to provide its own implementation of it (inherited automatically if the subclass adds none of its own designated initializers) — typically used when generic or protocol-oriented code needs to be able to construct Self regardless of which concrete subclass it holds.

Failable Initializers (init?, init!) and Enums with Raw Values

struct Animal {
    let species: String

    init?(species: String) {              // `init?`: initialization can fail and return nil instead of an instance
        if species.isEmpty { return nil }
        self.species = species
    }
}

let anonymous = Animal(species: "")        // nil -- the empty string fails validation
let giraffe = Animal(species: "Giraffe")   // an Animal?, non-nil

enum TemperatureUnit: Character {
    case kelvin = "K", celsius = "C", fahrenheit = "F"
}

let unit = TemperatureUnit(rawValue: "F")   // enums with raw values get a synthesized `init?(rawValue:)` for free

init? returns an Optional instance, nil-ing out via a bare return nil at any point when the arguments fail validation; init! behaves the same way but produces an implicitly-unwrapped optional instance. A failable initializer can delegate to (and be delegated to by) a non-failable one; a non-failable initializer can not call a failable one without unwrapping its result first. An enumeration with raw values (see Enumerations) automatically receives a synthesized init?(rawValue:), returning nil when no case has the given raw value.

Setting Defaults with Closures and Functions

struct Checkerboard {
    let boardColors: [Bool] = {                                 // an immediately-invoked closure computes the default
        var colors: [Bool] = []
        for row in 0..<8 {
            for column in 0..<8 {
                colors.append((row + column) % 2 == 0)
            }
        }
        return colors
    }()                                                          // the trailing `()` calls the closure right away
}

A stored property’s default value can be computed by a closure or function called immediately: the closure runs once, at the point the instance is initialized, with the trailing () invoking it there and then (omitting the parentheses would instead store the closure itself as the property’s value, which is never what’s wanted here). This is useful whenever a default requires more than a single expression to compute, without needing a full custom initializer just to set it.

Deinitialization (deinit)

class TemporaryFile {
    let path: String

    init(path: String) {
        self.path = path
        print("opened \(path)")
    }

    deinit {
        print("closing and removing \(path)")     // runs automatically, right before the instance is deallocated
    }
}

var file: TemporaryFile? = TemporaryFile(path: "/tmp/scratch")
file = nil        // "closing and removing /tmp/scratch" prints here, once nothing else references the instance

Only classes may declare a deinit, with no parentheses and no parameters, and a class may declare at most one. Swift calls it automatically, exactly once, immediately before an instance’s memory is deallocated — typically used to release a resource (a file handle, a network connection) the instance was managing, and never called directly. A subclass’s deinit runs before its superclass’s, and the superclass’s deinit always runs even if the subclass provides none of its own; how (and when) an instance actually reaches zero references in the first place is covered in Automatic Reference Counting. A noncopyable struct (~Copyable) can also declare deinit, for the same reason: see Structures and Classes.

See Also

  • Structures and Classes — default and memberwise initializers, and why only structs get the latter.

  • Inheritance — super, overriding, and the class hierarchy designated/convenience initializers delegate across.

  • Enumerations — raw values and init?(rawValue:).

  • Automatic Reference Counting — when an instance actually becomes eligible for deinitialization.

  • Properties — lazy properties, whose initial value is deferred past initialization entirely.