Angenommen, wir passen Ihr Protokoll für den Moment an, um eine Routine hinzuzufügen, die den zugehörigen Typ verwendet:
public protocol RequestType: class {
associatedtype Model
var path: String { get set }
func frobulateModel(aModel: Model)
}
Und Swift sollte es Ihnen ermöglichen, ein Array so zu erstellen, RequestType
wie Sie es möchten. Ich könnte ein Array dieser Anforderungstypen an eine Funktion übergeben:
func handleQueueOfRequests(queue: [RequestType]) {
// frobulate All The Things!
for request in queue {
request.frobulateModel(/* What do I put here? */)
}
}
Ich komme zu dem Punkt, an dem ich alle Dinge frobulieren möchte, aber ich muss wissen, welche Art von Argument in den Aufruf übergehen soll. Einige meiner RequestType
Entitäten könnten ein nehmen LegoModel
, einige könnten ein nehmen PlasticModel
, und andere könnten ein nehmen PeanutButterAndPeepsModel
. Swift ist mit der Mehrdeutigkeit nicht zufrieden, sodass Sie keine Variable eines Protokolls deklarieren können, dem ein Typ zugeordnet ist.
Gleichzeitig ist es durchaus sinnvoll, beispielsweise ein Array zu erstellen, RequestType
wenn wir wissen, dass alle das verwenden LegoModel
. Dies scheint vernünftig und ist es auch, aber Sie brauchen eine Möglichkeit, dies auszudrücken.
Eine Möglichkeit, dies zu tun, besteht darin, eine Klasse (oder Struktur oder Aufzählung) zu erstellen, die dem Namen des abstrakten Modelltyps einen realen Typ zuordnet:
class LegoRequestType: RequestType {
typealias Model = LegoModel
// Implement protocol requirements here
}
Jetzt ist es völlig vernünftig, eine Reihe von zu deklarieren, LegoRequestType
denn wenn wir frobulate
alle wollten, wissen wir, dass wir LegoModel
jedes Mal eine übergeben müssen.
Diese Nuance mit zugeordneten Typen macht jedes Protokoll, das sie verwendet, zu etwas Besonderem. Die Swift Standard Library verfügt über solche Protokolle, insbesondere Collection
oder Sequence
.
Damit Sie ein Array von Dingen erstellen können, die das Collection
Protokoll implementieren , oder eine Reihe von Dingen, die das Sequenzprotokoll implementieren, verwendet die Standardbibliothek eine Technik namens "Typlöschung", um die Strukturtypen AnyCollection<T>
oder zu erstellen AnySequence<T>
. Die Typ-Lösch-Technik ist in einer Stapelüberlauf-Antwort ziemlich komplex zu erklären, aber wenn Sie im Web suchen, gibt es viele Artikel darüber.
Ich kann ein Video von Alex Gallagher über Protokolle mit zugehörigen Typen (PATs) auf YouTube empfehlen .
Ab Swift 5.1
Sie können einen undurchsichtigen Ergebnistyp verwenden, um so etwas zu erreichen.
Stell dir das vor:
Folgendes erzeugt also den Fehler:
Wenn Sie den Typ jedoch durch Hinzufügen des Schlüsselworts vor dem Typ undurchsichtig machen,
some
wird das Problem behoben. In der Regel ist dies das einzige, was wir möchten:quelle
some
scheint als iOS 13+Eine kleine Änderung im Design Ihres Codes könnte dies ermöglichen. Fügen Sie oben in Ihrer Protokollhierarchie ein leeres, nicht zugeordnetes Typprotokoll hinzu. So was...
Ein weiteres Beispiel mit Klassen, die vom Protokoll RequestType abgeleitet sind, eine Warteschlange erstellen und die Warteschlange an eine Funktion übergeben, um den entsprechenden Typ zu drucken
quelle
Swift 5.1
Ein Beispiel , wie Sie verwenden können , generische Protokolle durch die Implementierung eine zugehörige Art und Basisprotokolls :
Und ein Beispiel für View Controller:
quelle
Dieser Fehler kann auch im folgenden Szenario auftreten:
In diesem Fall müssen Sie lediglich Generika verwenden, um das Problem zu beheben:
quelle