Blog

Die Geheimnisse der Swift-Eigentumsbeobachter lüften

Lammert Westerhoff

Aktualisiert Oktober 22, 2025
5 Minuten

Eine der coolen Funktionen von Swift sind Eigenschaftsbeobachter, vielleicht besser bekannt als willSet und didSet. Jeder, der in Swift programmiert, muss sie verwendet haben. Einige Leute mehr als andere. Und manche verwenden sie vielleicht ein bisschen zu viel, indem sie viele von ihnen zusammen ändern (mich manchmal eingeschlossen). Aber es ist nicht immer ganz offensichtlich, wann sie aufgerufen werden. Vor allem, wenn Sie mit struct zu tun haben, denn structs können etwas seltsam sein. Lassen Sie uns in einige Situationen eintauchen und sehen, was passiert.

Zuweisung

Die offensichtlichste Situation, in der didSet (und willSet) aufgerufen wird, ist die einfache Zuweisung einer Variablen. Stellen Sie sich die folgende Struktur vor:

struct Person {
  var name: String
  var age: Int
}

Und ein anderer Code, wie z.B. ein View-Controller, der es in einer Variablen mit einem Property Observer verwendet.

class MyViewController: UIViewController {
  var person: Person! {
  didSet {
  print("Person wurde auf '(Person.Name)' mit Alter (Person.Alter) gesetzt")
  }
  }
  override func viewDidLoad() {
  Person = Person(Name: "Bob", Alter: 20)
  }
}

Wie zu erwarten, wird der Code in didSet ausgeführt, wenn die Ansicht geladen wurde, und das Folgende wird auf der Konsole ausgegeben:

Person wurde mit Alter 20 auf 'Bob' gesetzt

Initialisierung

Auch dies ist ziemlich klar und Sie wissen es wahrscheinlich schon: Eigenschaftsbeobachter werden nicht ausgeführt, wenn die Variable bei der Initialisierung zugewiesen wird.

var person = Person (Name: "Bob", Alter: 20) {
  didSet {
  print("Person wurde auf '(Person.Name)' mit Alter (Person.Alter) gesetzt")
  }
  }

Dies führt zu keiner Ausgabe auf der Konsole. Auch wenn Sie Person in der init-Funktion zuweisen würden, wird willSet nicht aufgerufen.

Ändern von Strukturen

Weniger bekannt ist, dass Eigenschaftsbeobachter auch ausgeführt werden, wenn Sie die Memberwerte von Strukturen ändern, ohne die gesamte Struktur (neu) zuzuweisen. Das folgende Beispiel veranschaulicht dies.

class ViewController: UIViewController {
  var person = Person (Name: "Bob", Alter: 20) {
  didSet {
  print("Person wurde auf '(Person.Name)' mit Alter (Person.Alter) gesetzt")
  }
  }
  override func viewDidLoad() {
  person.name = "Mike"
  person.age = 30
  }
}

In diesem Beispiel haben wir die Person in unserer viewDidLoad-Funktion nie neu zugewiesen, aber durch die Änderung des Namens und des Alters wird willSet trotzdem zweimal ausgeführt und wir erhalten als Ausgabe:

Person wurde mit Alter 20 auf 'Mike' eingestellt
Person wurde mit 30 Jahren auf 'Mike' eingestellt

Funktionen mutieren

Was für das Ändern von Werten einer struct gilt, gilt auch für mutierende struct-Funktionen. Der Aufruf einer solchen Funktion führt immer dazu, dass die Eigenschaftsbeobachter einmal aufgerufen werden. Dabei spielt es keine Rolle, ob Sie die gesamte struct ersetzen (durch Zuweisung von self), mehrere Memberwerte ändern oder gar nichts ändern.

struct Person {
  var name: String
  var age: Int
  mutating func incrementAge() {
  wenn Alter  <  100 {
  Alter++
  }
  }
}

Hier haben wir eine Mutationsfunktion hinzugefügt, die das Alter erhöht, solange das Alter unter 100 liegt.

class ViewController: UIViewController {
  var person = Person (Name: "Bob", Alter: 98) {
  didSet {
  print("Person wurde auf '(Person.Name)' mit Alter (Person.Alter) gesetzt")
  }
  }
  override func viewDidLoad() {
  person.incrementAge()
  person.incrementAge()
  person.incrementAge()
  person.incrementAge()
  }
}

Unser willSet wird 4 Mal aufgerufen, obwohl sich bei den letzten beiden Malen nichts geändert hat.

Person wurde mit 99 Jahren auf 'Bob' eingestellt
Person wurde mit Alter 100 auf 'Bob' gesetzt
Person wurde mit Alter 100 auf 'Bob' gesetzt
Person wurde mit Alter 100 auf 'Bob' gesetzt

Änderungen innerhalb von Eigenschaftsbeobachtern

Es ist auch möglich, Änderungen an der Variablen innerhalb ihrer eigenen Eigenschaftsbeobachter vorzunehmen. Sie können die gesamte Variable neu zuweisen, ihre Werte ändern oder Mutationsfunktionen für sie aufrufen. Wenn Sie dies innerhalb eines Eigenschaftsbeobachters tun, werden die Eigenschaftsbeobachter nicht ausgelöst, da dies höchstwahrscheinlich zu einer Endlosschleife führen würde. Denken Sie daran, dass eine Änderung in willSet wirkungslos bleibt, da Ihre Änderung durch den Wert überschrieben wird, der ursprünglich gesetzt wurde (dies führt auch zu einer netten Warnung in Xcode).

class ViewController: UIViewController {
  var person = Person (Name: "Bob", Alter: 98) {
  didSet {
  print("Person wurde auf '(Person.Name)' mit Alter (Person.Alter) gesetzt")
  if person.name != oldValue.name {
  person.age = 0
  print("Das Alter der Person '(Person.Name)' wurde auf 0 gesetzt")
  }
  }
  }
  override func viewDidLoad() {
  person.name = "Mike"
  }
}

Warum es wichtig ist

Warum ist das alles so wichtig, werden Sie vielleicht denken. Nun, Sie müssen vielleicht überdenken, welche Art von Logik Sie in Ihre Eigenschaftsbeobachter einbauen und welche Sie außerhalb platzieren. Und all dies gilt auch für Arrays und Dictionaries, denn auch sie sind Strukturen. Nehmen wir an, Sie haben ein Array mit Zahlen, die sich ändern können, und jedes Mal, wenn sie sich ändern, möchten Sie Ihre Benutzeroberfläche aktualisieren. Aber Sie möchten die Zahlen auch sortieren. Der folgende Code sieht auf den ersten Blick vielleicht ganz gut aus:

class ViewController: UIViewController {
  var Zahlen: [Int] = [] {
  didSet {
  updateUI()
  }
  }
  override func viewDidLoad() {
  refreshNumbers()
  }
  func refreshNumbers() {
  Zahlen = [random() % 10, random() % 10, random() % 10, random() % 10]
  zahlen.sortInPlace()
  }
  func updateUI() {
  print("UI: (Zahlen)")
  }
}

Jedes Mal, wenn sich die Zahlen ändern, wird die Benutzeroberfläche aktualisiert. Da sortInPlace aber auch den Property Observer auslöst, wird die Benutzeroberfläche zweimal aktualisiert:

UI: [3, 6, 7, 5]
UI: [3, 5, 6, 7]

Wir sollten also sortInPlace in willSet einfügen, bevor wir updateUI aufrufen.

Verfasst von

Lammert Westerhoff

Contact

Let’s discuss how we can support your journey.