programowanieSenior iOS Developer

Jak realizuje się inicjalizację właściwości w Swift? Czym różnią się inicjalizatory designated, convenience i required? Jakie niuanse występują podczas pracy z hierarchiami inicjalizacji?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

W Swift wszystkie właściwości o typach nie opcjonalnych muszą być zainicjowane do końca inicjalizacji instancji. Używa się do tego inicjalizatorów: designated (główne), convenience (pomocnicze) i required (obowiązkowe dla dziedziczących).

  • Designated inicjalizator — główny, odpowiada za inicjalizację wszystkich właściwości klasy oraz wywołanie designated inicjalizatora nadklasy. Każda klasa ma co najmniej jeden designated inicjalizator.
  • Convenience inicjalizatory — pomocnicze, delegują inicjalizację tej samej klasy (wywołują inne inicjalizatory wewnątrz klasy przez self.init(...)), ułatwiając tworzenie obiektów z różnymi zestawami parametrów.
  • Required inicjalizator — wymaga, aby wszystkie podklasy zaimplementowały ten inicjalizator. Zadeklarowany z modyfikatorem required.

Przykład:

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" } }

Kluczowe cechy:

  • Wszystkie właściwości muszą być zainicjowane przed odwołaniem do super.init.
  • Inicjalizatory convenience zawsze muszą wywoływać designated lub inny convenience inicjalizator self.
  • Inicjalizatory required muszą być zaimplementowane we wszystkich dziedziczących klasach.

Pytanie z haczykiem.

W jakiej kolejności należy inicjalizować właściwości podklasy i nadklasy przy użyciu designated initializer?

Odpowiedź: Wszystkie właściwości podklasy muszą być zainicjowane przed wywołaniem designated inicjalizatora nadklasy (super.init), w przeciwnym razie wystąpi błąd kompilacji. Po super.init nie można inicjować nowych właściwości stored w podklasie, można tylko je używać.

class Parent { let parentProp: Int init(prop: Int) { parentProp = prop } } class Child: Parent { let childProp: String init(prop: Int, childProp: String) { self.childProp = childProp // Najpierw swoje właściwości super.init(prop: prop) // Potem parent } }

Przykłady rzeczywistych błędów spowodowanych brakiem znajomości niuansów tematu.


Historia

Programista w inicjalizatorze convenience próbował bezpośrednio przypisać wartość do właściwości required, nie wywołując designated. W wyniku tego właściwość została zainicjowana dwukrotnie z różnymi wartościami, co prowadziło do nieoczekiwanego zachowania podczas testowania.


Historia

Podczas dziedziczenia głęboko osadzonej hierarchii klas zapomniano zadeklarować required init, co spowodowało niemożność poprawnej inicjalizacji typu pochodnego z frameworka przez refleksję, oraz awarię podczas deserializacji modelu JSON.


Historia

W aplikacji podczas rozszerzania klasy Vehicle nie wywołano designated inicjalizatora nadklasy, co doprowadziło do niepoprawnej inicjalizacji obligatoryjnych właściwości i awarii w czasie działania w produkcji po aktualizacji schematu danych.