ProgrammierungSenior iOS Entwickler

Wie wird die Initialisierung von Eigenschaften in Swift umgesetzt? Was sind die Unterschiede zwischen designated, convenience und required Initialisierern? Welche Nuancen treten bei der Arbeit mit Initialisierungs-Hierarchien auf?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

In Swift müssen alle Eigenschaften vom Typ non-optional vor Abschluss der Instanziierung eines Objekts initialisiert werden. Dafür werden Initialisierer verwendet: designated (grundlegend), convenience (hilfreich) und required (für Nachfolger erforderlich).

  • Designated Initialisierer — grundlegend, verantwortlich für die Initialisierung aller Eigenschaften der Klasse und den Aufruf des designated Initialisierers der Superklasse. Jede Klasse hat mindestens einen designated Initialisierer.
  • Convenience Initialisierer — hilfreich, delegieren die Initialisierung an dieselbe Klasse (rufen andere Initialisierer innerhalb der Klasse über self.init(...) auf), was die Erstellung von Objekten mit verschiedenen Parameter-Sets vereinfacht.
  • Required Initialisierer — verlangt, dass alle Unterklassen diesen Initialisierer implementieren. Wird mit dem Modifikator required deklariert.

Beispiel:

class Vehicle { let numberOfWheels: Int required init(numberOfWheels: Int) { self.numberOfWheels = numberOfWheels } } class Car: Vehicle { let brand: String // Designated Initializer required init(numberOfWheels: Int) { self.brand = "Unknown" super.init(numberOfWheels: numberOfWheels) } // Convenience convenience init() { self.init(numberOfWheels: 4) self.brand = "Ford" } }

Wichtige Merkmale:

  • Alle Eigenschaften müssen vor dem Aufruf von super.init initialisiert werden.
  • Convenience Initialisierer müssen immer einen designated oder einen anderen convenience Initialisierer self aufrufen.
  • Required Initialisierer müssen in allen Nachfolgern implementiert werden.

Fangfrage.

In welcher Reihenfolge sollten die Eigenschaften der Unterklasse und der Superklasse bei der Verwendung des designated Initialisierers initialisiert werden?

Antwort: Alle Eigenschaften der Unterklasse müssen vor dem Aufruf des designated Initialisierers der Superklasse (super.init) initialisiert werden, andernfalls gibt es einen Kompilierungsfehler. Nach super.init dürfen keine neuen stored-Eigenschaften der Unterklasse mehr initialisiert werden, sondern nur verwendet werden.

class Parent { let parentProp: Int init(prop: Int) { parentProp = prop } } class Child: Parent { let childProp: String init(prop: Int, childProp: String) { self.childProp = childProp // Zuerst eigene Eigenschaften super.init(prop: prop) // Dann parent } }

Beispiele für reale Fehler aufgrund fehlenden Wissens über die Feinheiten des Themas.


Geschichte

Ein Entwickler versuchte in einem convenience Initialisierer, direkt einen Wert für eine required-Eigenschaft zuzuweisen, ohne den designated Initialisierer aufzurufen. Infolgedessen wurde die Eigenschaft zweimal mit unterschiedlichen Werten initialisiert, was während des Testens zu unerwartetem Verhalten führte.


Geschichte

Bei der Vererbung einer tiefen Klassenhierarchie wurde vergessen, den required init zu deklarieren, was zu einer nicht korrekten Initialisierung des abgeleiteten Typs aus dem Framework über Reflection führte und zu einem Absturz bei der Deserialisierung des JSON-Modells.


Geschichte

Während der Erweiterung der Klasse Vehicle wurde der designated Initialisierer der Superklasse nicht aufgerufen, was zu einer inkorrekten Initialisierung der erforderlichen Eigenschaften und einem Laufzeitabsturz in der Produktion nach dem Update des Datenschemas führte.