ProgrammatieiOS ontwikkelaar, Middle/Senior

Wat is type erasure in Swift, waarvoor is het nodig en hoe implementeer je het correct in de praktijk?

Slaag voor sollicitatiegesprekken met de Hintsage AI-assistent

Antwoord.

Achtergrond van de vraag: Type erasure (typewissen) is een patroon dat in Swift is ontstaan als antwoord op de beperkingen van protocollen met associated type of Self vereisten. Een protocol kan niet rechtstreeks als type worden gebruikt zonder type erasure als het verbonden types bevat, omdat de compiler de specifieke implementatie niet kent. Dit komt vaak voor bij het maken van datacontainers, verzamelingen, stroomlijnen, waar abstractie belangrijk is.

Probleem: Als er een protocol is met associatedtype, bijvoorbeeld:

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

kun je een variabele van het type Animal niet declareren. We hebben een "wisser wrapper" type nodig dat toegang biedt tot verschillende specifieke types via abstractie:

let zoo: [any Animal] // Compilatiefout

Oplossing: Type erasure wordt geïmplementeerd met behulp van wrappers (bijvoorbeeld het Box-patroon of struct AnyXxx), die een uniforme interface bieden en de details van het werkelijke type verbergen:

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

Nu kunnen verschillende implementaties van Animal met dezelfde Food worden opgeslagen:

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

Belangrijke kenmerken:

  • Hiermee kun je werken met protocollen met associatedtype als met een gewoon type
  • Lost het probleem op van generieke containers en fabrieken van protocolentiteiten
  • Is een architectonisch patroon voor het verbergen van implementatiedetails

Vragen met een valstrik.

Kan een protocol met associatedtype zonder type erasure worden gebruikt als type van een eigenschap of array?

Nee, dat kan niet. De compiler vereist specificatie van alle verbonden types. Bijvoorbeeld, de volgende notatie zal een fout veroorzaken:

let array: [Animal] // Fout: 'Animal' kan alleen worden gebruikt als een generieke beperking

Kan type erasure overerving vervangen?

Nee, type erasure is geen volledige vervanging voor overerving. Het lost de abstractieprobleem van protocollen met associatedtype op, terwijl overerving wordt gebruikt voor het implementeren van gedeelde logica en hergebruik van code tussen klassen.

Moet ik alle methoden en eigenschappen van het protocol binnen de type erasure wrapper implementeren?

Ja, absoluut. Type erasure werkt alleen als de wrapper volledig de externe interface van het protocol herhaalt, anders zal een deel van de functionaliteit niet beschikbaar zijn.

Typische fouten en anti-patronen

  • Vergeten alle methoden/eigenschappen te implementeren, wat leidt tot stille verlies van functionaliteit
  • Type erasure gebruiken waar het zonder kan (bijvoorbeeld als er geen associatedtype is)
  • Overbodige of te complexe wrappers creëren, wat de leesbaarheid vermindert

Voorbeeld uit het leven

Negatief geval

In een productieproject is al het werk met gegevensbronnen gebaseerd op type erasure, zelfs wanneer er geen behoefte is aan een associatedtype. De code wordt moeilijk te onderhouden, nieuwe ontwikkelaars raken in de war.

Voordelen:

  • Veelzijdigheid van de API

Nadelen:

  • Vermindering van de leesbaarheid, complicatie van de architectuur, latente bugs

Positief geval

Type erasure wordt alleen toegepast voor de abstractie van de gegevensbron, die verschillende laadstrategieën encapsuleert (netwerk, lokaal), elk met zijn eigen types. Verder is de code transparant.

Voordelen:

  • Flexibiliteit van de architectuur, eenvoud van integratie van nieuwe strategieën

Nadelen:

  • Voegt een laag tussenpersonen (wrappers) toe, wat het debuggen iets bemoeilijkt