Ich habe mich schließlich motiviert, meine Antwort zu aktualisieren. Sie können die Annahme überdenken.
Akashivskyy
@akashivskyy Ich akzeptiere Ihre Antwort, weil sie alle verfügbaren Optionen und ihre Vor- und Nachteile besser zeigt.
Selvin
Antworten:
504
1. Verwenden von Standardimplementierungen (bevorzugt).
protocolMyProtocol{func doSomething()}extensionMyProtocol{func doSomething(){/* return a default value or just leave empty */}}structMyStruct:MyProtocol{/* no compile error */}
Vorteile
Es ist keine Objective-C-Laufzeit beteiligt (zumindest nicht explizit). Dies bedeutet, dass Sie Strukturen, Aufzählungen und Nichtklassen daran anpassen NSObjectkönnen. Dies bedeutet auch, dass Sie das leistungsstarke Generika-System nutzen können.
Sie können immer sicher sein, dass alle Anforderungen erfüllt sind, wenn Sie auf Typen stoßen, die einem solchen Protokoll entsprechen. Es ist immer entweder eine konkrete Implementierung oder eine Standardimplementierung. So verhalten sich "Schnittstellen" oder "Verträge" in anderen Sprachen.
Nachteile
Für Nichtanforderungen Voidbenötigen Sie einen angemessenen Standardwert , der nicht immer möglich ist. Wenn Sie jedoch auf dieses Problem stoßen, bedeutet dies, dass entweder eine solche Anforderung eigentlich keine Standardimplementierung haben sollte oder dass Sie beim API-Design einen Fehler gemacht haben.
Sie können nicht zwischen einer Standardimplementierung und keiner Implementierung unterscheiden , zumindest ohne dieses Problem mit speziellen Rückgabewerten zu beheben. Betrachten Sie das folgende Beispiel:
Wenn Sie eine Standardimplementierung bereitstellen, die gerade zurückgegeben wird true, ist dies auf den ersten Blick in Ordnung. Betrachten Sie nun den folgenden Pseudocode:
finalclassSomeParser{func parse(data:Data)->[Any]{if/* delegate.validate(value:) is not implemented */{/* parse very fast without validating */}else{/* parse and validate every value */}}}
Es gibt keine Möglichkeit, eine solche Optimierung zu implementieren - Sie können nicht wissen, ob Ihr Delegat eine Methode implementiert oder nicht.
Obwohl es verschiedene Möglichkeiten gibt, dieses Problem zu lösen (mithilfe optionaler Schließungen, verschiedener Delegierungsobjekte für verschiedene Vorgänge, um nur einige zu nennen), zeigt dieses Beispiel das Problem deutlich.
2. Verwenden von @objc optional.
@objc protocolMyProtocol{@objc optionalfunc doSomething()}classMyClass:NSObject,MyProtocol{/* no compile error */}
Vorteile
Es ist keine Standardimplementierung erforderlich. Sie deklarieren einfach eine optionale Methode oder eine Variable und können loslegen.
Nachteile
Dies schränkt die Funktionen Ihres Protokolls erheblich ein , da alle konformen Typen Objective-C-kompatibel sein müssen. Dies bedeutet, dass nur Klassen, die von erben NSObject, einem solchen Protokoll entsprechen können. Keine Strukturen, keine Aufzählungen, keine zugehörigen Typen.
Sie müssen immer überprüfen, ob eine optionale Methode implementiert ist, indem Sie entweder optional aufrufen oder prüfen, ob der konforme Typ sie implementiert. Dies kann zu einer Menge Boilerplate führen, wenn Sie häufig optionale Methoden aufrufen.
Schauen Sie sich die verbesserte Möglichkeit optionaler Methoden in Swift mit einer Erweiterung (unten) an
Daniel Kanaan
1
Und wie testet man die Unterstützung einer optionalen Protokollmethode in einer Instanz? respondsToSelector?
Devios1
3
Tu das einfach nicht! Swift unterstützt aus einem bestimmten Grund keine optionale Methode in Protokollen.
fpg1503
2
Diese Methode unterstützt keine Optionen in Parametern. optional func doSomething(param: Int?)
Dh
4
Sicherlich ist diese Antwort heutzutage, Jahre später, im Wesentlichen falsch . Heute fügen Sie in Swift einfach eine Erweiterung für die Standardimplementierung hinzu. Dies ist ein grundlegender Aspekt von Swift. (Wie alle modernen Antworten unten zeigen.) Es wäre heutzutage einfach falsch, die objc-Flagge hinzuzufügen.
Fattie
394
Ab Swift 2 können Standardimplementierungen eines Protokolls hinzugefügt werden. Dies schafft eine neue Art optionaler Methoden in Protokollen.
protocolMyProtocol{func doSomethingNonOptionalMethod()func doSomethingOptionalMethod()}extensionMyProtocol{func doSomethingOptionalMethod(){// leaving this empty
}}
Es ist keine wirklich gute Möglichkeit, optionale Protokollmethoden zu erstellen, bietet Ihnen jedoch die Möglichkeit, Strukturen in Protokollrückrufen zu verwenden.
Dies ist wahrscheinlich der sauberste Weg, dies in Swift zu tun. Schade, dass es vor Swift 2.0 nicht funktioniert.
Entalpi
13
@MattQuiros Ich stelle fest, dass Sie die Funktion tatsächlich in der Protokolldefinition deklarieren müssen, da sonst die No-Op-Erweiterungsfunktion in Ihren Klassen, die dem Protokoll entsprechen, nicht überschrieben wird.
Ian Pearce
3
@IanPearce ist korrekt, und dies scheint beabsichtigt zu sein. Im Vortrag "Protocol Oriented Programming" (408) auf der WWDC wird über Methoden im Hauptprotokoll gesprochen, bei denen es sich um "Anpassungspunkte" handelt, die konformen Typen angeboten werden. Ein erforderlicher Anpassungspunkt erhält keine Definition in einer Erweiterung. ein optionaler Punkt tut. Methoden für das Protokoll, die im Allgemeinen nicht angepasst werden sollten, werden in der Erweiterung vollständig deklariert / definiert, damit konforme Typen nicht angepasst werden können, es sei denn, Sie setzen sie speziell auf ihren dynamicType um, um zu zeigen, dass Sie die benutzerdefinierte Implementierung des Konformers wünschen.
Matthias
4
@FranklinYu Sie können dies tun, aber dann verschmutzen Sie Ihr API-Design mit 'Würfen', wo es tatsächlich nicht benötigt wird. Ich mag eher die Idee von "Mikroprotokollen". ZB bestätigt jede Methode ein Protokoll und dann können Sie überprüfen, ob das Objekt Protokoll ist
Darko
3
@Antoine Ich denke, Sie sollten die Funktion in der Erweiterung öffentlich machen, da die Funktionen in den Protokollen per Definition öffentlich sind. Ihre Lösung funktioniert nicht, wenn das Protokoll außerhalb des Moduls verwendet wird.
Vadim Eisenberg
39
Da es einige Antworten zur Verwendung des optionalen Modifikators und des Attributs @objc zum Definieren des optionalen Anforderungsprotokolls gibt, werde ich ein Beispiel zur Verwendung der Protokollerweiterungen zum Definieren des optionalen Protokolls geben.
Der folgende Code ist Swift 3. *.
/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocolCancelable{/// default implementation is empty.
func cancel()}extensionCancelable{func cancel(){}}classPlane:Cancelable{//Since cancel() have default implementation, that is optional to class Plane
}let plane =Plane()
plane.cancel()//Print out *UnitedAirlines can't cancelable*
Bitte beachten Sie, dass Protokollerweiterungsmethoden nicht von Objective-C-Code aufgerufen werden können. Schlimmer ist, dass das Swift-Team dies nicht behebt. https://bugs.swift.org/browse/SR-492
Gut gemacht! Dies sollte die richtige Antwort sein, da das Problem dadurch gelöst wird, ohne dass die Objective-C-Laufzeit beeinträchtigt wird.
Lukas
wirklich schön!
Tania_S
aus der schnellen Dokumentation - "Protokollanforderungen mit Standardimplementierungen, die von Erweiterungen bereitgestellt werden, unterscheiden sich von optionalen Protokollanforderungen. Obwohl konforme Typen keine eigene Implementierung bereitstellen müssen, können Anforderungen mit Standardimplementierungen ohne optionale Verkettung aufgerufen werden."
Mec Os
34
Die anderen Antworten, bei denen das Protokoll als "@objc" markiert wird, funktionieren nicht, wenn schnelle spezifische Typen verwendet werden.
structInfo{var height:Intvar weight:Int}@objc protocolHealth{func isInfoHealthy(info:Info)->Bool}//Error"Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"
Um optionale Protokolle zu deklarieren, die mit Swift gut funktionieren, deklarieren Sie die Funktionen als Variablen anstelle von Funktionen.
classHuman:Health{var isInfoHealthy:(Info)->(Bool)?={ info inif info.weight <200&& info.height >72{returntrue}returnfalse}//Or leave out the implementation and declare it as:
//var isInfoHealthy: (Info) -> (Bool)?
}
Sie können dann "?" um zu überprüfen, ob die Funktion implementiert wurde oder nicht
Nur Klassen, Protokolle, Methoden und Eigenschaften können @objc verwenden. Wenn Sie einen Enum-Parameter in der Definition der @ objc-Protokollmethode verwenden, sind Sie zum Scheitern verurteilt.
Khunshan
1
@khunshan, für diese Methode muss nichts mit @ objc markiert sein. Worauf beziehen Sie sich?
Zag
Es handelt sich um eine Information zu dem Thema, dass Enums nicht zwischen swift und objc verwendet werden können. Diese für andere Anweisungen können mit dem Schlüsselwort @objc überbrückt werden.
Khunshan
34
Hier ist ein konkretes Beispiel mit dem Delegierungsmuster.
Setzen Sie den Delegaten auf eine Klasse und implementieren Sie das Protokoll. Stellen Sie sicher, dass die optionale Methode nicht implementiert werden muss.
Ich denke, bevor Sie fragen, wie Sie eine optionale Protokollmethode implementieren können, sollten Sie sich fragen, warum Sie eine implementieren sollten.
Wenn wir schnelle Protokolle als Schnittstelle in der klassischen objektorientierten Programmierung betrachten, sind optionale Methoden wenig sinnvoll, und eine bessere Lösung wäre möglicherweise, eine Standardimplementierung zu erstellen oder das Protokoll in eine Reihe von Protokollen zu unterteilen (möglicherweise mit einigen Vererbungsbeziehungen) zwischen ihnen), um die mögliche Kombination von Methoden im Protokoll darzustellen.
Hier ist ein sehr einfaches Beispiel NUR für schnelle Klassen und nicht für Strukturen oder Aufzählungen. Beachten Sie, dass die Protokollmethode optional ist und zwei Ebenen der optionalen Verkettung im Spiel hat. Außerdem benötigt die Klasse, die das Protokoll übernimmt, das Attribut @objc in ihrer Deklaration.
@objc protocolCollectionOfDataDelegate{optionalfunc indexDidChange(index:Int)}@objc classRootView:CollectionOfDataDelegate{var data =CollectionOfData()init(){
data.delegate =self
data.indexIsNow()}func indexDidChange(index:Int){
println("The index is currently: \(index)")}}classCollectionOfData{var index :Int?weakvar delegate :CollectionOfDataDelegate?func indexIsNow(){
index =23
delegate?.indexDidChange?(index!)}}
Können Sie den Teil " Zwei Ebenen des optionalen Spiels " etwas genauer beschreiben , nämlich : delegate?.indexDidChange?(index!)?
Unheilig
2
Wenn wir das Protokoll so geschrieben hätten, dass es eine nicht optionale Methode wie diese enthält protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } , würden Sie es ohne das Fragezeichen aufrufen: delegate?.indexDidChange(index!) Wenn Sie eine optionale Anforderung für eine Methode in einem Protokoll festlegen, implementiert der Typ, der dieser Methode entspricht, diese Methode möglicherweise NICHT wird also ?verwendet, um nach der Implementierung zu suchen. Wenn keine vorhanden ist, stürzt das Programm nicht ab. @ Unheilig
Blessing Lopes
weak var delegate : CollectionOfDataDelegate?(Schwache Referenz sicherstellen?)
Alfie Hanssen
@BlessingLopes Können Sie delegate?Ihrer Antwort Ihre Erklärung zur Verwendung hinzufügen ? Diese Informationen sollten in Zukunft wirklich für andere da sein. Ich möchte dies positiv bewerten, aber diese Informationen sollten wirklich in der Antwort enthalten sein.
Johnathon Sullinger
3
Wenn Sie dies in reiner Schnelligkeit tun möchten, ist es am besten, eine Standardimplementierung bereitzustellen, insbesondere wenn Sie einen Swift-Typ wie z. B. struct zurückgeben mit Swift-Typen zurückgeben
Es gibt zwei Möglichkeiten, wie Sie eine optionale Methode im Swift-Protokoll erstellen können.
1 - Die erste Option besteht darin, Ihr Protokoll mit dem Attribut @objc zu markieren. Dies bedeutet zwar, dass es nur von Klassen übernommen werden kann, bedeutet jedoch, dass Sie einzelne Methoden wie folgt als optional markieren:
2 - Ein schnellerer Weg: Diese Option ist besser. Schreiben Sie Standardimplementierungen der optionalen Methoden, die so nichts tun.
protocolMyProtocol{func optionalMethod()func notOptionalMethod()}extensionMyProtocol{func optionalMethod(){//this is a empty implementation to allow this method to be optional
}}
Swift verfügt über eine Funktion namens Erweiterung, mit der wir eine Standardimplementierung für die Methoden bereitstellen können, die optional sein sollen.
Definieren Sie die Funktion im Protokoll und erstellen Sie eine Erweiterung für dieses Protokoll. Erstellen Sie dann eine leere Implementierung für die Funktion, die Sie als Option verwenden möchten.
Um OptionalProtocolschnell zu definieren , sollten Sie das @objcSchlüsselwort vor der ProtocolDeklaration und attribute/ oder methodDeklaration innerhalb dieses Protokolls verwenden. Unten finden Sie ein Beispiel für die optionale Eigenschaft eines Protokolls.
@objc protocolProtocol{@objc optionalvar name:String?}classMyClass:Protocol{// No error
}
Während dies die Frage beantworten kann, ist es besser, eine Beschreibung hinzuzufügen, wie diese Antwort zur Lösung des Problems beitragen kann. Bitte lesen Sie Wie schreibe ich eine gute Antwort , um mehr zu erfahren ?
Roshana Pitigala
-23
Stellen Sie die @optionalvor Methoden oder Eigenschaften.
Antworten:
1. Verwenden von Standardimplementierungen (bevorzugt).
Vorteile
Es ist keine Objective-C-Laufzeit beteiligt (zumindest nicht explizit). Dies bedeutet, dass Sie Strukturen, Aufzählungen und Nichtklassen daran anpassen
NSObject
können. Dies bedeutet auch, dass Sie das leistungsstarke Generika-System nutzen können.Sie können immer sicher sein, dass alle Anforderungen erfüllt sind, wenn Sie auf Typen stoßen, die einem solchen Protokoll entsprechen. Es ist immer entweder eine konkrete Implementierung oder eine Standardimplementierung. So verhalten sich "Schnittstellen" oder "Verträge" in anderen Sprachen.
Nachteile
Für Nichtanforderungen
Void
benötigen Sie einen angemessenen Standardwert , der nicht immer möglich ist. Wenn Sie jedoch auf dieses Problem stoßen, bedeutet dies, dass entweder eine solche Anforderung eigentlich keine Standardimplementierung haben sollte oder dass Sie beim API-Design einen Fehler gemacht haben.Sie können nicht zwischen einer Standardimplementierung und keiner Implementierung unterscheiden , zumindest ohne dieses Problem mit speziellen Rückgabewerten zu beheben. Betrachten Sie das folgende Beispiel:
Wenn Sie eine Standardimplementierung bereitstellen, die gerade zurückgegeben wird
true
, ist dies auf den ersten Blick in Ordnung. Betrachten Sie nun den folgenden Pseudocode:Es gibt keine Möglichkeit, eine solche Optimierung zu implementieren - Sie können nicht wissen, ob Ihr Delegat eine Methode implementiert oder nicht.
Obwohl es verschiedene Möglichkeiten gibt, dieses Problem zu lösen (mithilfe optionaler Schließungen, verschiedener Delegierungsobjekte für verschiedene Vorgänge, um nur einige zu nennen), zeigt dieses Beispiel das Problem deutlich.
2. Verwenden von
@objc optional
.Vorteile
Nachteile
Dies schränkt die Funktionen Ihres Protokolls erheblich ein , da alle konformen Typen Objective-C-kompatibel sein müssen. Dies bedeutet, dass nur Klassen, die von erben
NSObject
, einem solchen Protokoll entsprechen können. Keine Strukturen, keine Aufzählungen, keine zugehörigen Typen.Sie müssen immer überprüfen, ob eine optionale Methode implementiert ist, indem Sie entweder optional aufrufen oder prüfen, ob der konforme Typ sie implementiert. Dies kann zu einer Menge Boilerplate führen, wenn Sie häufig optionale Methoden aufrufen.
quelle
respondsToSelector
?optional func doSomething(param: Int?)
Ab Swift 2 können Standardimplementierungen eines Protokolls hinzugefügt werden. Dies schafft eine neue Art optionaler Methoden in Protokollen.
Es ist keine wirklich gute Möglichkeit, optionale Protokollmethoden zu erstellen, bietet Ihnen jedoch die Möglichkeit, Strukturen in Protokollrückrufen zu verwenden.
Ich habe hier eine kleine Zusammenfassung geschrieben: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
quelle
Da es einige Antworten zur Verwendung des optionalen Modifikators und des Attributs @objc zum Definieren des optionalen Anforderungsprotokolls gibt, werde ich ein Beispiel zur Verwendung der Protokollerweiterungen zum Definieren des optionalen Protokolls geben.
Der folgende Code ist Swift 3. *.
Bitte beachten Sie, dass Protokollerweiterungsmethoden nicht von Objective-C-Code aufgerufen werden können. Schlimmer ist, dass das Swift-Team dies nicht behebt. https://bugs.swift.org/browse/SR-492
quelle
Die anderen Antworten, bei denen das Protokoll als "@objc" markiert wird, funktionieren nicht, wenn schnelle spezifische Typen verwendet werden.
Um optionale Protokolle zu deklarieren, die mit Swift gut funktionieren, deklarieren Sie die Funktionen als Variablen anstelle von Funktionen.
Implementieren Sie dann das Protokoll wie folgt
Sie können dann "?" um zu überprüfen, ob die Funktion implementiert wurde oder nicht
quelle
Hier ist ein konkretes Beispiel mit dem Delegierungsmuster.
Richten Sie Ihr Protokoll ein:
Setzen Sie den Delegaten auf eine Klasse und implementieren Sie das Protokoll. Stellen Sie sicher, dass die optionale Methode nicht implementiert werden muss.
Eine wichtige Sache ist, dass die optionale Methode optional ist und ein "?" beim anrufen. Erwähnen Sie das zweite Fragezeichen.
quelle
In Swift 3.0
Das spart Zeit.
quelle
@objc
jetzt eine Quelle für das Warum aller Mitglieder?required
flag versucht , aber mit Fehlern:required
darf nur für 'init'-Deklarationen verwendet werden.optional
vor jeder Methode ein Schlüsselwort hinzufügen .quelle
@objc
nicht nur als Protokoll markieren .Ein reiner Swift-Ansatz mit Protokollvererbung:
quelle
Um die Mechanik von Antoines Antwort zu veranschaulichen:
quelle
Ich denke, bevor Sie fragen, wie Sie eine optionale Protokollmethode implementieren können, sollten Sie sich fragen, warum Sie eine implementieren sollten.
Wenn wir schnelle Protokolle als Schnittstelle in der klassischen objektorientierten Programmierung betrachten, sind optionale Methoden wenig sinnvoll, und eine bessere Lösung wäre möglicherweise, eine Standardimplementierung zu erstellen oder das Protokoll in eine Reihe von Protokollen zu unterteilen (möglicherweise mit einigen Vererbungsbeziehungen) zwischen ihnen), um die mögliche Kombination von Methoden im Protokoll darzustellen.
Weitere Informationen finden Sie unter https://useyourloaf.com/blog/swift-optional-protocol-methods/ , das einen hervorragenden Überblick zu diesem Thema bietet.
quelle
Etwas abseits des Themas von der ursprünglichen Frage, aber es baut auf Antoines Idee auf und ich dachte, es könnte jemandem helfen.
Sie können berechnete Eigenschaften auch für Strukturen mit Protokollerweiterungen optional machen.
Sie können eine Eigenschaft für das Protokoll optional machen
Implementieren Sie die berechnete Dummy-Eigenschaft in der Protokollerweiterung
Und jetzt können Sie Strukturen verwenden, bei denen die optionale Eigenschaft implementiert ist oder nicht
Ich habe in meinem Blog auch beschrieben, wie optionale Eigenschaften in Swift-Protokollen ausgeführt werden. Diese werden auf dem neuesten Stand gehalten, falls sich durch die Swift 2-Versionen etwas ändert.
quelle
So erstellen Sie optionale und erforderliche Delegierungsmethoden.
quelle
Hier ist ein sehr einfaches Beispiel NUR für schnelle Klassen und nicht für Strukturen oder Aufzählungen. Beachten Sie, dass die Protokollmethode optional ist und zwei Ebenen der optionalen Verkettung im Spiel hat. Außerdem benötigt die Klasse, die das Protokoll übernimmt, das Attribut @objc in ihrer Deklaration.
quelle
delegate?.indexDidChange?(index!)
?protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) }
, würden Sie es ohne das Fragezeichen aufrufen:delegate?.indexDidChange(index!)
Wenn Sie eine optionale Anforderung für eine Methode in einem Protokoll festlegen, implementiert der Typ, der dieser Methode entspricht, diese Methode möglicherweise NICHT wird also?
verwendet, um nach der Implementierung zu suchen. Wenn keine vorhanden ist, stürzt das Programm nicht ab. @ Unheiligweak var delegate : CollectionOfDataDelegate?
(Schwache Referenz sicherstellen?)delegate?
Ihrer Antwort Ihre Erklärung zur Verwendung hinzufügen ? Diese Informationen sollten in Zukunft wirklich für andere da sein. Ich möchte dies positiv bewerten, aber diese Informationen sollten wirklich in der Antwort enthalten sein.Wenn Sie dies in reiner Schnelligkeit tun möchten, ist es am besten, eine Standardimplementierung bereitzustellen, insbesondere wenn Sie einen Swift-Typ wie z. B. struct zurückgeben mit Swift-Typen zurückgeben
Beispiel:
dann können Sie Protokoll ohne definiert jede implementieren func
quelle
Es gibt zwei Möglichkeiten, wie Sie eine optionale Methode im Swift-Protokoll erstellen können.
1 - Die erste Option besteht darin, Ihr Protokoll mit dem Attribut @objc zu markieren. Dies bedeutet zwar, dass es nur von Klassen übernommen werden kann, bedeutet jedoch, dass Sie einzelne Methoden wie folgt als optional markieren:
2 - Ein schnellerer Weg: Diese Option ist besser. Schreiben Sie Standardimplementierungen der optionalen Methoden, die so nichts tun.
Swift verfügt über eine Funktion namens Erweiterung, mit der wir eine Standardimplementierung für die Methoden bereitstellen können, die optional sein sollen.
quelle
Eine Möglichkeit besteht darin, sie als optionale Funktionsvariablen zu speichern:
quelle
Definieren Sie die Funktion im Protokoll und erstellen Sie eine Erweiterung für dieses Protokoll. Erstellen Sie dann eine leere Implementierung für die Funktion, die Sie als Option verwenden möchten.
quelle
Um
Optional
Protocol
schnell zu definieren , sollten Sie das@objc
Schlüsselwort vor derProtocol
Deklaration undattribute
/ odermethod
Deklaration innerhalb dieses Protokolls verwenden. Unten finden Sie ein Beispiel für die optionale Eigenschaft eines Protokolls.quelle
Stellen Sie die
@optional
vor Methoden oder Eigenschaften.quelle
@optional
ist nicht einmal das richtige Schlüsselwort. Es istoptional
, und Sie müssen die Klasse und das Protokoll mit dem@objc
Attribut deklarieren .