ProgrammationDéveloppeur iOS Senior

Comment se fait l'initialisation des propriétés en Swift ? Quelles sont les différences entre les initialisateurs designated, convenience et required ? Quels sont les points délicats à prendre en compte lors de la gestion des hiérarchies d'initialisation ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

En Swift, toutes les propriétés de types non-optionnels doivent être initialisées avant la fin de l'initialisation d'une instance. Pour cela, on utilise des initialisateurs : designated (principaux), convenience (secondaires) et required (obligatoires pour les sous-classes).

  • Initialiseur Designated — principal, il est responsable de l'initialisation de toutes les propriétés de la classe et de l'appel de l'initialiseur designated de la superclasse. Chaque classe a au moins un initialiseur designated.
  • Initialisateurs Convenience — auxiliaires, ils délèguent l'initialisation à la même classe (appellent d'autres initialisateurs à l'intérieur de la classe via self.init(...)), simplifiant la création d'un objet avec différents ensembles de paramètres.
  • Initialiseur Required — exige que toutes les sous-classes implémentent cet initialiseur. Il est déclaré avec le modificateur required.

Exemple :

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

Caractéristiques clés :

  • Toutes les propriétés doivent être initialisées avant l'appel à super.init.
  • Les initialisateurs convenience doivent toujours appeler un initialiseur designated ou un autre initialiseur convenience self.
  • Les initialisateurs required doivent être implémentés dans toutes les sous-classes.

Question piégeuse.

Dans quel ordre doit-on initialiser les propriétés de la sous-classe et de la super-classe lors de l'utilisation d'un initialiseur designated ?

Réponse : Toutes les propriétés de la sous-classe doivent être initialisées avant d'appeler l'initialiseur designated de la super-classe (super.init), sinon il y aura une erreur de compilation. Après super.init, il n'est pas possible d'initialiser de nouvelles propriétés stockées de la sous-classe, uniquement de les utiliser.

class Parent { let parentProp: Int init(prop: Int) { parentProp = prop } } class Child: Parent { let childProp: String init(prop: Int, childProp: String) { self.childProp = childProp // D'abord ses propres propriétés super.init(prop: prop) // Ensuite parent } }

Exemples d'erreurs réelles dues à une méconnaissance des subtilités du sujet.


Histoire

Un développeur dans l'initialiseur convenience a essayé d'assigner directement une valeur à une propriété required sans appeler designated. En conséquence, la propriété a été initialisée deux fois avec des valeurs différentes, entraînant un comportement inattendu lors des tests.


Histoire

Lors de l'héritage d'une hiérarchie de classes profonde, on a oublié de déclarer required init, ce qui a rendu impossible l'initialisation correcte du type dérivé depuis un framework via reflection, entraînant un crash lors de la désérialisation d'un modèle JSON.


Histoire

Dans l'application, lors de l'extension de la classe Vehicle, l'initialiseur designated de la super-classe n'a pas été appelé, ce qui a entraîné une initialisation incorrecte des propriétés obligatoires et un crash runtime en production après la mise à jour du schéma de données.