ProgrammationDéveloppeur iOS, Middle/Senior

Qu'est-ce que l'effacement de type en Swift, à quoi cela sert et comment le mettre en œuvre correctement en pratique ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question : L'effacement de type (type erasure) est un patron qui est apparu en Swift comme une réponse aux limitations des protocoles avec des types associés ou des exigences Self. Un protocole ne peut pas être utilisé comme type directement sans effacement de type, s'il contient des types associés, car le compilateur ne connaît pas la mise en œuvre concrète. Cela est souvent rencontré lors de la création de conteneurs de données, de collections, de flux, où l'abstraction est importante.

Problème : Si nous avons un protocole avec un associatedtype, par exemple :

protocol Animal { associatedtype Food func eat(_ food: Food) }

il est impossible de déclarer une variable de type Animal. Nous avons besoin d'un type "effaçant" qui permet d'accéder à différents types concrets via l'abstraction :

let zoo: [any Animal] // Erreur de compilation

Solution : L'effacement de type est réalisé à l'aide d'enveloppes (par exemple, le patron Box ou struct AnyXxx), fournissant une interface unique tout en cachant les détails du type réel :

struct AnyAnimal<F>: Animal { private let _eat: (F) -> Void init<A: Animal>(_ base: A) where A.Food == F { _eat = base.eat } func eat(_ food: F) { _eat(food) } }

Maintenant, nous pouvons conserver différentes implémentations d'Animal avec le même Food :

let animals: [AnyAnimal<Grass>] = [AnyAnimal(Cow()), AnyAnimal(Sheep())]

Caractéristiques clés :

  • Permet de travailler avec des protocoles avec un associatedtype comme avec un type ordinaire
  • Résout le problème des conteneurs et des usines d'entités protocolaires universelles
  • Constitue un patron architectural pour cacher les détails de l'implémentation

Questions pièges.

Peut-on utiliser un protocole avec un associatedtype sans effacement de type comme type de propriété ou de tableau ?

Non, ce n'est pas possible. Le compilateur exige la spécification de tous les types associés. Par exemple, l'enregistrement suivant générera une erreur :

let array: [Animal] // Erreur : 'Animal' ne peut être utilisé qu'en tant que contrainte générique

L'effacement de type peut-il remplacer l'héritage ?

Non, l'effacement de type n'est pas un remplacement complet de l'héritage. Il résout le problème d'abstraction pour les protocoles avec associatedtype, tandis que l'héritage est utilisé pour implémenter une logique commune et réutiliser du code entre les classes.

Doit-on implémenter tous les méthodes et propriétés du protocole à l'intérieur de l'enveloppe d'effacement de type ?

Oui, absolument. L'effacement de type fonctionne uniquement si l'enveloppe réplique complètement l'interface externe du protocole, sinon une partie de la fonctionnalité sera inaccessible.

Erreurs courantes et anti-patrons

  • Oublient d'implémenter toutes les méthodes/propriétés, ce qui entraîne une perte silencieuse de fonctionnalité
  • Utilisent l'effacement de type là où cela pourrait être évité (par exemple, s'il n'y a pas d'associatedtype)
  • Créent des enveloppes redondantes ou trop complexes, diminuant la lisibilité

Exemple de la vie réelle

Cas négatif

Dans un projet en production, tout le travail avec les sources de données est basé sur l'effacement de type, même lorsqu'il n'y a pas besoin d'associatedtype. Le code devient difficile à maintenir, les nouveaux développeurs se perdent.

Avantages :

  • Universalité de l'API

Inconvénients :

  • Diminution de la lisibilité, complexité de l'architecture, bug latents

Cas positif

L'effacement de type est appliqué uniquement pour l'abstraction de la source de données, qui encapsule différentes stratégies de chargement (réseau, local), chacune avec ses propres types. Pour le reste, le code est transparent.

Avantages :

  • Flexibilité de l'architecture, simplicité d'intégration de nouvelles stratégies

Inconvénients :

  • Ajoute une couche d'intermédiaires (enveloppes), ce qui complique légèrement le débogage