Protokolle mit associatedtype und where-Beschränkungen sind ein leistungsfähiger Mechanismus zur Typabstraktion in Swift. Dies wird häufig zur Implementierung generischer Protokolle verwendet, bei denen der spezifische Typ durch die Implementierung festgelegt wird. Historisch konnten Protokolle mit associatedtype in früheren Versionen von Swift nicht als konkrete Typen (existential types) verwendet werden, was Einschränkungen bei der Verwendung solcher Protokolle in Sammlungen und Schnittstellen zur Folge hatte. Später fügte Swift Mechanismen hinzu, um associatedtype mit where-Bedingungen einzuschränken, was die Erstellung flexibler und sicherer Abstraktionen für komplexe Szenarien ermöglicht.
Problem: Häufig besteht die Aufgabe darin, ein Protokoll zu erstellen, das es ermöglicht, sich von bestimmten Sammlungen oder Datenverarbeitern abzugrenzen. Standardprotokolle können jedoch nicht flexibel genug sein: Nicht alle Typen können ohne Kenntnis der verwandten Typen verallgemeinert werden, und die Logik zur Vererbung von Protokollen mit associatedtype ist nicht immer offensichtlich.
Lösung: Verwenden Sie where-Beschränkungen, um die Anforderungen an die Implementierungen zu präzisieren, sodass Protokolle mit anpassbarem Verhalten erstellt werden können.
Beispiel Code:
protocol Storage { associatedtype Element func add(_ item: Element) } // Eingeschränktes Protokoll für die Speicherung nur von Zahlen protocol NumericStorage: Storage where Element: Numeric { func sum() -> Element } struct IntStorage: NumericStorage { private var items: [Int] = [] func add(_ item: Int) { items.append(item) } func sum() -> Int { items.reduce(0, +) } }
Wesentliche Merkmale:
Kann man ein Protokoll mit associatedtype als Typ verwenden (zum Beispiel für eine Sammlung)?
Nein, das kann man nicht direkt. Ein Protokoll mit associatedtype ist ein PAT (protokoll mit assoziiertem Typ) und kann nicht als existential type verwendet werden. Es ist beispielsweise nicht möglich, ein Array [Storage] zu deklarieren, das geht nur durch Typverzerrung.
Wie implementiert man die Typverzerrung für ein Protokoll mit associatedtype?
Durch eine Hilfsverpackung, die den tatsächlichen Typ der Implementierung verbirgt.
struct AnyStorage<T>: Storage { private let _add: (T) -> Void init<S: Storage>(_ storage: S) where S.Element == T { _add = storage.add } func add(_ item: T) { _add(item) } }
Was ist der Unterschied zwischen associatedtype und einem generischen Parameter eines Protokolls?
associatedtype definiert einen konkreten Typ, der entsprechend implementiert werden muss, während ein generischer Parameter explizit im Protokoll oder in der Funktion angegeben wird, aber nicht in der Protokoll-Deklaration erlaubt ist. Ein Protokoll kann nicht generisch im Syntax sein, nur über associatedtype.
Ein Entwickler versuchte, [Storage] für die Speicherung beliebiger Sammlungen zu verwenden. Der Code kompiliert nicht, und es sind implizite Typumwandlungen oder die Verwendung von Any/unsafe Ansätzen erforderlich.
Vorteile:
Nachteile:
Ein Entwickler gestaltete AnyStorage<T>, um die konkrete Implementierung zu verbergen, und fügte where-Beschränkungen hinzu, um sicherzustellen, dass nur die richtigen Typen korrekt funktionieren.
Vorteile:
Nachteile: