Geschichte des Themas:
In Swift bis Version 5 gab es beim Zurückgeben eines Wertes, der einem Protokoll mit associated type entspricht, häufig Einschränkungen — der Typ konnte nicht direkt als Rückgabetyp verwendet werden, es war Type Erasure erforderlich. Um die Lesbarkeit und Leistung zu verbessern, wurden opaque types eingeführt — Rückgabewerte über das Schlüsselwort some, die es ermöglichen, Abstraktionen in öffentlichen Schnittstellen zu beschreiben.
Problem:
Wenn man den tatsächlichen Typ des zurückgegebenen Wertes verbergen möchte, während man die Abstraktion über ein Protokoll beibehält, aber die Kosten von dynamic dispatch und type erasure vermeiden möchte. Zum Beispiel beim Zurückgeben von Sammlungen, Sequenzen, View-Komponenten.
Lösung:
Opaque types ermöglichen es, einen Typ, der einem Protokoll entspricht, zurückzugeben, während seine konkrete Implementierung verborgen bleibt. Der Compiler kennt den tatsächlichen Typ, aber die aufrufende Seite nicht.
Beispiel:
protocol Shape { func area() -> Double } struct Circle: Shape { var radius: Double func area() -> Double { Double.pi * radius * radius } } func makeCircle() -> some Shape { return Circle(radius: 3) } let s = makeCircle() print(s.area()) // funktioniert
Wichtige Merkmale:
someWie unterscheidet sich opaque type (some Protocol) vom Rückgabetyp Protocol?
Opaque type hat zur Compile-Zeit eine konkrete Implementierung (obwohl sie von außen verborgen ist). Bei der Rückgabe von Protocol wird dynamic dispatch angewendet, es gibt keine type safety, wenn ein associated type vorhanden ist.
Kann man unterschiedliche Typen mit some Protocol in einer Funktion zurückgeben?
Nein. Alle Rückgaben müssen denselben tatsächlichen Typ zurückgeben:
func maker(flag: Bool) -> some Shape { if flag { return Circle(radius: 3) } else { return Square(size: 2) // Fehler: Rückgabetyp stimmt nicht überein } }
Kann associatedtype innerhalb von Protocol über some Protocol verwendet werden?
Ja. Genau dafür (vor allem) ist opaque type gedacht:
protocol View { associatedtype Body } func makeView() -> some View { /* ... */ }
Die Funktion gibt ein Protokoll ohne opaque zurück, was die Verwendung von Methoden mit associated type nicht erlaubt, komplizierte type erasure erforderlich macht, der Code kompiliert nicht oder arbeitet suboptimal.
Vorteile:
Nachteile:
ViewBuilder in SwiftUI verwendet some View und verbirgt Details, erhöht die Typensicherheit, verbessert die Kompilierungsgeschwindigkeit und Runtime.
Vorteile:
Nachteile: