ProgrammazioneSenior iOS Developer

Come viene implementata l'inizializzazione delle proprietà in Swift? Quali sono le differenze tra gli inizializzatori designated, convenience e required? Quali sfide si presentano durante il lavoro con le gerarchie di inizializzazione?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

In Swift tutte le proprietà di tipo non opzionale devono essere inizializzate entro la fine dell'inizializzazione dell'istanza. Per fare ciò, si utilizzano gli inizializzatori: designated (principali), convenience (ausiliari) e required (obbligatori per i sottoclassi).

  • Designated inizializzatore — principale, si occupa di inizializzare tutte le proprietà della classe e di chiamare l'inizializzatore designated della superclasse. Ogni classe ha almeno un inizializzatore designated.
  • Convenience inizializzatori — ausiliari, delegano l'inizializzazione a questo stesso classe (chiamano altri inizializzatori all'interno della classe tramite self.init(...)), semplificando la creazione di un oggetto con diversi set di parametri.
  • Required inizializzatore — richiede che tutti i sottoclassi implementino questo inizializzatore. Viene dichiarato con il modificatore required.

Esempio:

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

Caratteristiche principali:

  • Tutte le proprietà devono essere inizializzate prima di accedere a super.init.
  • Gli inizializzatori convenience devono sempre chiamare un inizializzatore designated o un altro inizializzatore convenience self.
  • Gli inizializzatori required devono essere implementati in tutti i sottoclassi.

Domanda insidiosa.

In quale ordine devono essere inizializzate le proprietà del sottoclasse e della superclasse quando si utilizza l'inizializzatore designated?

Risposta: Tutte le proprietà del sottoclasse devono essere inizializzate prima di chiamare l'inizializzatore designated della superclasse (super.init), altrimenti ci sarà un errore di compilazione. Dopo super.init non è possibile inizializzare nuove proprietà memorizzate del sottoclasse, solo usarle.

class Parent { let parentProp: Int init(prop: Int) { parentProp = prop } } class Child: Parent { let childProp: String init(prop: Int, childProp: String) { self.childProp = childProp // Prima le proprie proprietà super.init(prop: prop) // Poi parent } }

Esempi di errori reali dovuti alla mancanza di conoscenza delle sfumature dell'argomento.


Storia

Uno sviluppatore nel convenience inizializzatore ha cercato di assegnare direttamente un valore a una proprietà richiesta senza chiamare il designated. Di conseguenza, la proprietà è stata inizializzata due volte con valori diversi, il che ha portato a comportamenti imprevisti durante i test.


Storia

Durante l'ereditarietà di una gerarchia di classi profonda, si è dimenticato di dichiarare required init, il che ha portato all'impossibilità di inizializzare correttamente il tipo derivato da un framework tramite reflection, causando un crash durante la deserializzazione del modello JSON.


Storia

Nell'app durante l'estensione della classe Vehicle, non è stato chiamato l'inizializzatore designated della superclasse, il che ha portato a un'inizializzazione errata delle proprietà obbligatorie e a un crash durante il runtime in produzione dopo l'aggiornamento dello schema dei dati.