Hintergrund: Type Erasure (Typ-Löschung) ist ein Muster, das in Swift als Antwort auf die Einschränkungen von Protokollen mit associated type oder Self-Anforderungen entstand. Ein Protokoll kann nicht direkt als Typ verwendet werden, ohne Type Erasure, wenn es verbundene Typen enthält, da der Compiler die spezifische Implementierung nicht kennt. Dies wird oft bei der Erstellung von Dat Containern, Sammlungen, Strömen, wo Abstraktion wichtig ist, festgestellt.
Problem: Wenn es ein Protokoll mit associatedtype gibt, zum Beispiel:
protocol Animal { associatedtype Food func eat(_ food: Food) }
kann man keine Variable vom Typ Animal deklarieren. Wir benötigen einen "Löschhüllentyp", der es ermöglicht, auf verschiedene konkrete Typen über Abstraktion zuzugreifen:
let zoo: [any Animal] // Kompilierfehler
Lösung: Type Erasure wird mit Hilfe von Wrappern (z.B. Box-Muster oder struct AnyXxx) implementiert, die eine einheitliche Schnittstelle bereitstellen und die Details des tatsächlichen Typs 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) } }
Jetzt können verschiedene Implementierungen von Animal mit demselben Food gespeichert werden:
let animals: [AnyAnimal<Grass>] = [AnyAnimal(Cow()), AnyAnimal(Sheep())]
Wichtige Merkmale:
Kann man ein Protokoll mit associatedtype ohne Type Erasure als Typ von Eigenschaft oder Array verwenden?
Nein, das geht nicht. Der Compiler verlangt die Spezifikation aller verbundenen Typen. Zum Beispiel wird die folgende Notation einen Fehler verursachen:
let array: [Animal] // Fehler: 'Animal' kann nur als allgemeine Einschränkung verwendet werden
Kann Type Erasure die Vererbung ersetzen?
Nein, Type Erasure ist kein vollständiger Ersatz für Vererbung. Es löst das Abstraktionsproblem für Protokolle mit associatedtype, während die Vererbung zur Implementierung gemeinsamer Logik und Wiederverwendung von Code zwischen Klassen verwendet wird.
Müssen alle Methoden und Eigenschaften des Protokolls im Type Erasure Wrapper implementiert werden?
Ja, unbedingt. Type Erasure funktioniert nur, wenn der Wrapper das externe Interface des Protokolls vollständig wiederholt, sonst wird ein Teil der Funktionalität nicht verfügbar sein.
In einem Produktionsprojekt erfolgt die gesamte Arbeit mit Datenquellen über Type Erasure, selbst wenn kein Bedarf an associatedtype besteht. Der Code wird schwer wartbar, neue Entwickler sind verwirrt.
Vorteile:
Nachteile:
Type Erasure wird nur zur Abstraktion der Datenquelle verwendet, die verschiedene Lade-Strategien (netzwertig, lokal) kapselt, jede mit ihren Typen. Ansonsten ist der Code transparent.
Vorteile:
Nachteile: