In Swift kann ich den Typ einer Variablen explizit festlegen, indem ich ihn wie folgt deklariere:
var object: TYPE_NAME
Wenn wir noch einen Schritt weiter gehen und eine Variable deklarieren möchten, die mehreren Protokollen entspricht, können wir das protocol
Deklarativ verwenden:
var object: protocol<ProtocolOne,ProtocolTwo>//etc
Was ist, wenn ich ein Objekt deklarieren möchte, das einem oder mehreren Protokollen entspricht und auch von einem bestimmten Basisklassentyp ist? Das Objective-C-Äquivalent würde folgendermaßen aussehen:
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
In Swift würde ich erwarten, dass es so aussieht:
var object: TYPE_NAME,ProtocolOne//etc
Dies gibt uns die Flexibilität, die Implementierung des Basistyps sowie die im Protokoll definierte zusätzliche Schnittstelle durchführen zu können.
Gibt es einen anderen offensichtlicheren Weg, den ich vermissen könnte?
Beispiel
Angenommen, ich habe eine UITableViewCell
Fabrik, die für die Rückgabe von Zellen gemäß einem Protokoll verantwortlich ist. Wir können leicht eine generische Funktion einrichten, die Zellen zurückgibt, die einem Protokoll entsprechen:
class CellFactory {
class func createCellForItem<T: UITableViewCell where T:MyProtocol >(item: SpecialItem,tableView: UITableView) -> T {
//etc
}
}
Später möchte ich diese Zellen aus der Warteschlange entfernen und dabei sowohl den Typ als auch das Protokoll nutzen
var cell: MyProtocol = CellFactory.createCellForItem(somethingAtIndexPath) as UITableViewCell
Dies gibt einen Fehler zurück, da eine Tabellenansichtszelle nicht dem Protokoll entspricht ...
Ich möchte angeben können, dass die Zelle a ist UITableViewCell
und der MyProtocol
in der Variablendeklaration entspricht.
Rechtfertigung
Wenn Sie mit dem Factory-Muster vertraut sind, ist dies sinnvoll, wenn Sie Objekte einer bestimmten Klasse zurückgeben können, die eine bestimmte Schnittstelle implementieren.
Genau wie in meinem Beispiel möchten wir manchmal Schnittstellen definieren, die sinnvoll sind, wenn sie auf ein bestimmtes Objekt angewendet werden. Mein Beispiel für die Zelle der Tabellenansicht ist eine solche Rechtfertigung.
Der angegebene Typ entspricht zwar nicht genau der genannten Schnittstelle, das von der Factory zurückgegebene Objekt jedoch. Daher möchte ich die Flexibilität bei der Interaktion sowohl mit dem Basisklassentyp als auch mit der deklarierten Protokollschnittstelle
quelle
NSSomething<ABCProtocolOne,ABCProtocolTwo> * object = ...;
. Dieses Objekt scheint ziemlich nutzlos zu sein, da esNSSomething
bereits weiß, wozu es passt. Wenn es entspricht nicht einem der Protokolle in<>
Sie bekommenunrecognised selector ...
stürzt ab. Dies bietet überhaupt keine Typensicherheit.Antworten:
In Swift 4 ist es jetzt möglich, eine Variable zu deklarieren, die eine Unterklasse eines Typs ist und gleichzeitig ein oder mehrere Protokolle implementiert.
So führen Sie eine optionale Variable aus:
oder als Parameter einer Methode:
Apple kündigte dies auf der WWDC 2017 in Sitzung 402 an: Was ist neu in Swift?
quelle
Sie können keine Variable wie deklarieren
noch deklarieren Funktionstyp wie
Sie können als Funktionsparameter wie diesen deklarieren, aber es ist im Grunde ein Upcasting.
Ab sofort können Sie nur noch Folgendes tun:
Damit ist technisch
cell
identisch mitasProtocol
.Aber was den Compiler betrifft,
cell
hat erUITableViewCell
nur eine Schnittstelle von , während erasProtocol
nur eine Protokollschnittstelle hat. Wenn Sie also dieUITableViewCell
Methoden aufrufen möchten , müssen Sie einecell
Variable verwenden. Wenn Sie die Protokollmethode aufrufen möchten, verwenden SieasProtocol
variable.Wenn Sie sicher sind, dass die Zelle den Protokollen entspricht, müssen Sie sie nicht verwenden
if let ... as? ... {}
. mögen:quelle
-> UITableViewCell<MyProtocol>
ist dies ungültig, daUITableViewCell
es sich nicht um einen generischen Typ handelt. Ich denke, das wird nicht einmal kompiliert.cell
gibt es nur Protokollmethoden (für Compiler).Leider unterstützt Swift keine Protokollkonformität auf Objektebene. Es gibt jedoch eine etwas umständliche Problemumgehung, die Ihren Zwecken dienen kann.
Überall dort, wo Sie etwas tun müssen, über das UIViewController verfügt, greifen Sie auf den .viewController-Aspekt der Struktur zu, und auf alles, was Sie für den Protokollaspekt benötigen, verweisen Sie auf das .protocol.
Zum Beispiel:
Immer wenn Sie mySpecialViewController benötigen, um etwas mit UIViewController zu tun, verweisen Sie einfach auf mySpecialViewController.viewController, und wenn Sie eine Protokollfunktion benötigen, verweisen Sie auf mySpecialViewController.protocol.
Hoffentlich können wir mit Swift 4 in Zukunft ein Objekt mit daran angehängten Protokollen deklarieren. Aber im Moment funktioniert das.
Hoffe das hilft!
quelle
Vielleicht irre ich mich, aber reden Sie nicht darüber, der
UITableCellView
Klasse Protokollkonformität hinzuzufügen ? Das Protokoll wird in diesem Fall auf die Basisklasse und nicht auf das Objekt erweitert. Weitere Informationen finden Sie in der Dokumentation von Apple zum Deklarieren der Protokollannahme mit einer Erweiterung, die in Ihrem Fall ungefähr so aussehen würde:Neben der bereits erwähnten Swift-Dokumentation finden Sie im Artikel Allgemeine Funktionen für inkompatible Typen von Nate Cooks weitere Beispiele.
Die Protokollübernahme führt genau dies aus und bewirkt, dass ein Objekt das angegebene Protokoll einhält. Beachten Sie jedoch der negativen Seite, dass eine Variable eines bestimmten Protokolltyps ist nicht alles wissen , außerhalb des Protokolls. Dies kann jedoch umgangen werden, indem ein Protokoll definiert wird, das alle erforderlichen Methoden / Variablen / ...
Wenn Sie eine generische Methode wünschen, deren Variable sowohl einem Protokoll- als auch einem Basisklassentyp entspricht, haben Sie möglicherweise kein Glück. Es hört sich jedoch so an, als müssten Sie das Protokoll breit genug definieren, um über die erforderlichen Konformitätsmethoden zu verfügen, und gleichzeitig eng genug, um es ohne zu viel Arbeit in Basisklassen übernehmen zu können (dh nur zu erklären, dass eine Klasse dem entspricht Protokoll).
quelle
Ich hatte einmal eine ähnliche Situation, als ich versuchte, meine generischen Interaktorverbindungen in Storyboards zu verknüpfen (IB erlaubt Ihnen nicht, Outlets mit Protokollen zu verbinden, sondern nur mit Objektinstanzen) Eigentum. Dies hindert zwar niemanden daran, per se illegale Zuweisungen vorzunehmen, bietet jedoch eine bequeme Möglichkeit, unerwünschte Interaktionen mit einer nicht konformen Instanz zur Laufzeit sicher zu verhindern. (dh verhindern, dass Delegatenmethoden an Objekte aufgerufen werden, die nicht dem Protokoll entsprechen.)
Beispiel:
Der "outputReceiver" wird als optional deklariert, ebenso wie der private "protocolOutputReceiver". Indem ich immer über diesen (die berechnete Eigenschaft) auf den outputReceiver (auch bekannt als delegate) zugreife, filtere ich effektiv alle Objekte heraus, die nicht dem Protokoll entsprechen. Jetzt kann ich einfach die optionale Verkettung verwenden, um das delegierte Objekt sicher aufzurufen, ob es das Protokoll implementiert oder überhaupt existiert.
Um dies auf Ihre Situation anzuwenden, können Sie den öffentlichen ivar vom Typ "YourBaseClass?" (im Gegensatz zu AnyObject) und verwenden Sie die private berechnete Eigenschaft, um die Protokollkonformität zu erzwingen. FWIW.
quelle