ProgrammierungiOS Entwickler, Middle/Senior

Was sind opaque types (some) in Swift, wozu sind sie gut und wann sollten sie anstelle von protocol types verwendet werden?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Historie der Frage: Opaque types (some) wurden mit Swift 5.1 eingeführt und eröffnen eine neue Möglichkeit zur Abstraktion des Rückgabetyps einer Funktion oder Eigenschaft, wenn der Typ dem Compiler bekannt, aber dem Benutzer verborgen ist. Dies ist eine Alternative zu protocol existentials (any Protocol), aber mit fester Bindung an einen bestimmten Typ innerhalb der Funktion.

Problem: Wenn eine Funktion ein Protokoll mit associatedtype zurückgibt (z.B. Sequence), kann man nicht direkt schreiben:

func makeNumberSequence() -> Sequence { ... } // Fehler

Protokoll-Existentials erlauben es, jede Implementierung zurückzugeben, garantieren jedoch nicht, dass bei jedem Aufruf derselbe Typ zurückgegeben wird:

func foo() -> any View { ... }

Dies führt zu Unvorhersehbarkeiten und schwachem type safety.

Lösung: Verwendung eines opaque result type:

func makeNumbers() -> some Sequence { [1, 2, 3] }

Jetzt weiß der Compiler genau, welcher Rückgabewert tatsächlich ist, aber er ist von außen verborgen. Dies ermöglicht Leistungsoptimierungen, Sicherheit, die Verwendung von SwiftUI DSL und erleichtert den Typaustausch zwischen Modulen.

Wesentliche Eigenschaften:

  • Der konkrete Typ innerhalb des opaque Typs ist immer derselbe
  • Unterstützt kein associatedtype an der Stelle der Verwendung, sondern nur in der Definition
  • Wird verwendet, um deklarative APIs zu erstellen (z.B. SwiftUI)

Tricks bei Fragen.

Können opaque types zur Speicherung von Werten (wie Klassenproperties) verwendet werden?

Nein. Opaque types gelten nur für Rückgabewerte von Funktionen oder berechneten Eigenschaften. Zur Speicherung von Werten oder Wertarrays verwendet man Existentials (any Protocol).

Können verschiedene Zweige einer Funktion unterschiedliche Typen mit some zurückgeben?

Nein. Der Compiler verlangt, dass beide (oder alle) Zweige denselben konkreten Typ zurückgeben, andernfalls gibt es einen Fehler:

func foo(flag: Bool) -> some Sequence { if flag { return [1, 2, 3] } else { return ["a", "b", "c"] // Fehler } }

Kann man some verwenden, um den Rückgabewerttyp zwischen mehreren Funktionen zu identifizieren?

Nein. Jede Funktion mit some gibt ihren eigenen einzigartigen versteckten Typ zurück, selbst wenn es sich tatsächlich um dasselbe Array handelt. Das Ergebnis einer Funktion kann nicht als Parameter in einer anderen Funktion verwendet werden, wenn beide some mit unterschiedlichen Protokollen oder unterschiedlichen versteckten Typen verwenden.

Typische Fehler und Anti-Patterns

  • Versuchen, unterschiedliche Typen durch some zurückzugeben (Kompilierungsfehler)
  • Verwenden von some, wo protocol existentials erforderlich sind (z.B. zur Speicherung von Zuständen)
  • Versäumen von Optimierungen, indem sie weiterhin old-style protocol types verwenden

Beispiel aus dem Leben

Negativer Fall

Im Projekt wird alles über any Protocol zurückgegeben, Sammlungen verlieren ihren Typ, es treten Bugs beim Downcasting auf, die Kompilierungsoptimierung wird verlangsamt.

Vorteile:

  • Flexible Architektur mit der Möglichkeit, verschiedene Typen zu kombinieren

Nachteile:

  • Verlust der type safety, Verringerung der Effizienz, Probleme mit casts

Positiver Fall

In SwiftUI-Design geben Komponenten some View zurück, jedes Modul definiert den inneren Typ klar. Die Bundle-Größe wird reduziert, der Build wird schneller.

Vorteile:

  • Verbesserung der Leistung, Erhöhung der type safety

Nachteile:

  • Etwas Verlust der Flexibilität bei der Notwendigkeit dynamischer Speicherung