Ist es in Swift möglich? Wenn nicht, gibt es eine Problemumgehung?
Ist es in Swift möglich? Wenn nicht, gibt es eine Problemumgehung?
Antworten:
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: 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 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:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}
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:
final class SomeParser {
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.
@objc optional
.@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
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.
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.
protocol MyProtocol {
func doSomethingNonOptionalMethod()
func doSomethingOptionalMethod()
}
extension MyProtocol {
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.
Ich habe hier eine kleine Zusammenfassung geschrieben: https://www.avanderlee.com/swift-2-0/optional-protocol-methods/
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()`
protocol Cancelable {
/// default implementation is empty.
func cancel()
}
extension Cancelable {
func cancel() {}
}
class Plane: Cancelable {
//Since cancel() have default implementation, that is optional to class Plane
}
let plane = Plane()
plane.cancel()
// Print out *United Airlines 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
Die anderen Antworten, bei denen das Protokoll als "@objc" markiert wird, funktionieren nicht, wenn schnelle spezifische Typen verwendet werden.
struct Info {
var height: Int
var weight: Int
}
@objc protocol Health {
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.
protocol Health {
var isInfoHealthy: (Info) -> (Bool)? { get set }
}
Implementieren Sie dann das Protokoll wie folgt
class Human: Health {
var isInfoHealthy: (Info) -> (Bool)? = { info in
if info.weight < 200 && info.height > 72 {
return true
}
return false
}
//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
func returnEntity() -> Health {
return Human()
}
var anEntity: Health = returnEntity()
var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))?
//"isHealthy" is true
Hier ist ein konkretes Beispiel mit dem Delegierungsmuster.
Richten Sie Ihr Protokoll ein:
@objc protocol MyProtocol:class
{
func requiredMethod()
optional func optionalMethod()
}
class MyClass: NSObject
{
weak var delegate:MyProtocol?
func callDelegate()
{
delegate?.requiredMethod()
delegate?.optionalMethod?()
}
}
Setzen Sie den Delegaten auf eine Klasse und implementieren Sie das Protokoll. Stellen Sie sicher, dass die optionale Methode nicht implementiert werden muss.
class AnotherClass: NSObject, MyProtocol
{
init()
{
super.init()
let myInstance = MyClass()
myInstance.delegate = self
}
func requiredMethod()
{
}
}
Eine wichtige Sache ist, dass die optionale Methode optional ist und ein "?" beim anrufen. Erwähnen Sie das zweite Fragezeichen.
delegate?.optionalMethod?()
In Swift 3.0
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
Das spart Zeit.
@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 .@objc
nicht nur als Protokoll markieren .
Um die Mechanik von Antoines Antwort zu veranschaulichen:
protocol SomeProtocol {
func aMethod()
}
extension SomeProtocol {
func aMethod() {
print("extensionImplementation")
}
}
class protocolImplementingObject: SomeProtocol {
}
class protocolImplementingMethodOverridingObject: SomeProtocol {
func aMethod() {
print("classImplementation")
}
}
let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()
noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"
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.
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
protocol SomeProtocol {
var required: String { get }
var optional: String? { get }
}
Implementieren Sie die berechnete Dummy-Eigenschaft in der Protokollerweiterung
extension SomeProtocol {
var optional: String? { return nil }
}
Und jetzt können Sie Strukturen verwenden, bei denen die optionale Eigenschaft implementiert ist oder nicht
struct ConformsWithoutOptional {
let required: String
}
struct ConformsWithOptional {
let required: String
let optional: String?
}
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.
So erstellen Sie optionale und erforderliche Delegierungsmethoden.
@objc protocol InterViewDelegate:class {
@objc optional func optfunc() // This is optional
func requiredfunc()// This is required
}
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 protocol CollectionOfDataDelegate{
optional func indexDidChange(index: Int)
}
@objc class RootView: CollectionOfDataDelegate{
var data = CollectionOfData()
init(){
data.delegate = self
data.indexIsNow()
}
func indexDidChange(index: Int) {
println("The index is currently: \(index)")
}
}
class CollectionOfData{
var index : Int?
weak var delegate : CollectionOfDataDelegate?
func indexIsNow(){
index = 23
delegate?.indexDidChange?(index!)
}
}
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. @ Unheilig
weak 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:
struct magicDatas {
var damagePoints : Int?
var manaPoints : Int?
}
protocol magicCastDelegate {
func castFire() -> magicDatas
func castIce() -> magicDatas
}
extension magicCastDelegate {
func castFire() -> magicDatas {
return magicDatas()
}
func castIce() -> magicDatas {
return magicDatas()
}
}
dann können Sie Protokoll ohne definiert jede implementieren func
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:
@objc protocol MyProtocol {
@objc optional func optionalMethod()
}
2 - Ein schnellerer Weg: Diese Option ist besser. Schreiben Sie Standardimplementierungen der optionalen Methoden, die so nichts tun.
protocol MyProtocol {
func optionalMethod()
func notOptionalMethod()
}
extension MyProtocol {
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.
Eine Möglichkeit besteht darin, sie als optionale Funktionsvariablen zu speichern:
struct MyAwesomeStruct {
var myWonderfulFunction : Optional<(Int) -> Int> = nil
}
let squareCalculator =
MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)
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 Optional
Protocol
schnell zu definieren , sollten Sie das @objc
Schlüsselwort vor der Protocol
Deklaration und attribute
/ oder method
Deklaration innerhalb dieses Protokolls verwenden. Unten finden Sie ein Beispiel für die optionale Eigenschaft eines Protokolls.
@objc protocol Protocol {
@objc optional var name:String?
}
class MyClass: Protocol {
// No error
}
Stellen Sie die @optional
vor Methoden oder Eigenschaften.
@optional
ist nicht einmal das richtige Schlüsselwort. Es ist optional
, und Sie müssen die Klasse und das Protokoll mit dem @objc
Attribut deklarieren .