Init-Funktionen in Swift

article headerAktualisiert am 01. Oktober 2017


Die Init-Funktionen (Initializer) dienen dazu die Eigenschaften (Properties) eine neuen Datentyps zu initialisieren. In anderen Programmiersprachen kennt man sie unter der Bezeichnung Konstruktoren.

Init-Funktionen in Klassen

In einer Klasse ist eine Init-Funktion immer dann erforderlich, wenn den Variablen keine Werte zugeordnet worden sind. Im folgenden Beispiel bedarf es also keiner Init-Funktion:

class Person {

    let name: String = "Peter"
    let age: Int = 34

}

Bei folgendem Code würde sich der Compiler aber beschweren:

class Person {

    let name: String
    let age: Int

}

Es erscheint die Fehlermeldung: “Class ‘Person’ has no initializers”. Da keine Standardwerte für die Eigenschaften name und age angegeben wurden, bedarf es hier einer Init-Funktion, so wie im folgenden Beispiel gezeigt:

class Person {

    let name: String
    let age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

Wir Ihr sehen könnt, kommt in der Init-Funktion das Schlüsselwort self vor. Es verweist auf den Namen der Eigenschaft (Property), also name und age.

In vielen Fällen bedarf es des Schlüsselwortes self nicht, denn der Compiler ist häufig in der Lage zu erkennen, was gemeint ist. Dies gilt grundsätzlich auch für Init-Funktionen. Aufgrund der Gleichheit von Eigenschaftsnamen und Parameternamen, muss hier aber self verwendet werden. Anders wäre es hingegen, wenn ich die Init-Funktion wie folgt geschrieben hätte:

init(a: String, b: Int) {
    name = a
    age = b
}

Anstatt einer Eigenschaft einen Wert zuzuweisen, kann man eine Variable auch als optionalen Typ deklarieren:

class Person {

    var name: String
    var age: Int? // optionaler Typ

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

Ein optionaler Typ wird automatisch mit nil initialisiert. Es bedarf dann keiner Initialisierung mittels einer Init-Funktion. Dementsprechend wird im gezeigten Beispiel nur die Variable name mit der Init-Funktion initialisiert.

Bei der bisher gezeigten Init-Funktion handelt es sich um einen sogenannten Designated Initializer (auch: Designated Init Function). Damit ist eine Init-Funktion gemeint, mit der alle Eigenschaften initialisiert werden.

Eine Klasse kann (muss aber nicht) weitere Init-Funktionen enthalten. Weitere Init-Funktionen werden als Convenience Initializer bezeichnet. Sie dienen nicht dazu, die Eigenschaften zu initialisieren, vielmehr rufen sie statt dessen mit self.init den designated initializer auf:

class Person {

    let name: String
    let age: Int

    // designated initializer
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    // convenience initializer
    convenience init(name: String) {
        self.init(name: name, age: 27)
    }
}

var person = Person(name: "Peter")
let age = person.age // -> 27
let name = person.name // -> "Peter"

In diesem Beispiel wird eine Instanz mit dem Wert “27” für die Eigenschaft age initialisiert. Ein Convenience Initializer könnte also dazu genutzt werden, Standardwerte für Eigenschaften festzulegen.

Init-Funktionen in Strukturen

Betrachten wir folgendes Beispiel:

struct Rectangle {

    var a: Double
    var b: Double

}

let r = Rectangle(a: 4.0, b: 7.5)
r.a // -> 4
r.b // -> 7.5

Obwohl hier den Variablen keine Werte zugeordnet wurden, beschwert sich der Compiler nicht (anders als dies bei einer Klasse der Fall gewesen wäre). Das hängt damit zusammen, dass bei Strukturen automatic initializers zum Einsatz kommen, mit der eine Instanz erzeugt werden kann.

Es besteht aber die Möglichkeit, eigene Init-Funktionen zu schreiben. Sobald man das macht, wird keine automatische Init-Funktion verwendet:

 struct Rectangle {

    var a: Double
    var b: Double

    init(a: Double, b: Double) {
        self.a = a
        self.b = b
    }
}

Wie bei den Referenztypen (also Klassen), können auch bei Strukturen mehrere Init-Funktionen geschrieben werden:

struct Staatsoberhaupt {
    let firstName: String
    let lastName: String

    init() {
        firstName = "Frank-Walter"
        lastName = "Steinmeier"
    }

    init(firstName: String, lastName: String) {
        self.firstName = firstName
        self.lastName = lastName
    }
}

Es ist auch möglich, dass eine Init-Funktion eine andere Init-Funktion aufruft (Initializer Delegation):

struct Staatsoberhaupt {

    let firstName: String
    let lastName: String

    init() {
        self.init(firstName: "Frank-Walter", lastName: "Steinmeier")
    }

    init(firstName: String, lastName: String) {
        self.init(firstName: firstName, lastName: lastName)
    }
}

Im Gegensatz zu Klassen, wird hier aber nicht convenience vor init gesetzt; ein schlichtes init() { ... } reicht aus.

Was gibt es noch?

Euch werden auch noch andere Init-Funktionen begegnen. Da wären z.B. die Required Init Functions und die Failable Init Functions, die ich in diesem Beitrag lediglich kurz erwähnen möchte.

Required Initializer

Der Verwendung des Modifizierers required bei einer Init-Funktion führt dazu, das alle Subklassen diesen Initialisierer implementieren müssen.

class Employee {

    let placeOfBirth: String

    // required init function
    required init?(placeOfBirth: String) {
        self.placeOfBirth = placeOfBirth
    }
}

Würde man nun eine Subklasse von Employee schreiben, würde die Verwendung einer “schlichten” Init-Funktion den Compiler zu einer Fehlermeldung veranlassen: “required initializer must be provided by subclass of…”; erforderlich ist nämlich ein required init().

Failable Initializer

Eine Init-Funktion kann mit einem Fragezeichen als Optional gekennzeichnet werden. Als Beispiel greife ich hier auf eine Abwandlung des Beispiels aus Apples Swift-Dokumentation zurück:

class Developer {

var age: Int!

    // failable init function
    init?(age: Int) {
        self.age = age
        if age < 22 {
            return nil
        }
    }
}

let d = Developer(age: 17) // -> nil

Wie das Beispiel verdeutlicht, kann damit überprüft werden, ob ein übergebener Parameter gewünscht ist. Hier soll age größer als “22” sein, andernfalls wird nil zurückgegeben.

Deinit-Funktion

Und dann wäre da noch die – nur bei Klassen vorhandene – Deinit-Funktion. Sie wird kurz vor der Entfernung einer Instanz aus dem Speicher ausgeführt. Damit könnte man beispielsweise überprüfen, ob eine Instanz tatsächlich entfernt wurde:

deinit {
    print("Info: \(person) is being deinitialized!")
}

Durch Benutzung dieser Website erklären Sie sich mit der Verwendung von Cookies einverstanden. Mehr Informationen

Die Verwendung von Cookies dient dazu, Inhalte und Anzeigen zu personalisieren, Funktionen für soziale Medien anbieten zu können und die Zugriffe auf diese Website zu analysieren. Außerdem werden Informationen zur Nutzung dieser Webseite an Partner für soziale Medien, Werbung und Analysen weitergegeben.

Schließen