433 Stimmen

Wie deklariert man optionale Methoden in einem Swift-Protokoll?

Ist dies möglich in Swift? Wenn nicht, gibt es dann eine Möglichkeit, es zu umgehen?

657voto

akashivskyy Punkte 42822

1. Verwendung von Standardimplementierungen (bevorzugt).

protocol MyProtocol {
    func doSomething()
}

extension MyProtocol {
    func doSomething() {
        /* Gib einen Standardwert zurück oder lasse es einfach leer */
    }
}

struct MyStruct: MyProtocol {
    /* kein Kompilierfehler */
}

Vorteile

  • Es ist kein Objective-C-Laufzeitcode involviert (nun ja, zumindest nicht explizit). Das bedeutet, dass Sie Strukturen, Enums und Nicht-NSObject-Klassen daran anpassen können. Außerdem können Sie das leistungsstarke Generiksystem nutzen.

  • Sie können immer sicher sein, dass alle Anforderungen erfüllt sind, wenn Sie auf Typen treffen, die einem solchen Protokoll entsprechen. Es handelt sich immer entweder um eine konkrete Implementierung oder eine Standardimplementierung. So verhalten sich "Interfaces" oder "Contracts" in anderen Sprachen.

Nachteile

  • Für Anforderungen, die nicht Void sind, benötigen Sie einen vernünftigen Standardwert, was nicht immer möglich ist. Wenn Sie jedoch auf dieses Problem stoßen, bedeutet dies entweder, dass eine solche Anforderung wirklich keine Standardimplementierung haben sollte, oder dass Sie einen Fehler bei der API-Entwicklung gemacht haben.

  • Sie können nicht zwischen einer Standardimplementierung und gar keiner Implementierung unterscheiden, zumindest nicht, ohne das Problem mit speziellen Rückgabewerten anzugehen. Betrachten Sie das folgende Beispiel:

    protocol SomeParserDelegate {
        func validate(value: Any) -> Bool
    }

    Wenn Sie eine Standardimplementierung bereitstellen, die einfach true zurückgibt - scheint das auf den ersten Blick in Ordnung zu sein. Betrachten Sie jetzt den folgenden Pseudocode:

    final class SomeParser {
        func parse(data: Data) -> [Any] {
            if /* delegate.validate(value:) ist nicht implementiert */ {
                /* sehr schnell ohne Validierung parsen */
            } else {
                /* jeden Wert parsen und validieren */
            }
        }
    }

    Es gibt keine Möglichkeit, eine solche Optimierung zu implementieren - Sie können nicht wissen, ob Ihr Delegierter eine Methode implementiert oder nicht.

    Obwohl es verschiedene Möglichkeiten gibt, dieses Problem zu überwinden (z. B. Verwendung optionaler Closures, verschiedene Delegiertenobjekte für verschiedene Operationen zu benennen), zeigt das Beispiel das Problem deutlich.


2. Verwendung von @objc optional.

@objc protocol MyProtocol {
    @objc optional func doSomething()
}

class MyClass: NSObject, MyProtocol {
    /* kein Kompilierfehler */
}

Vorteile

  • Es wird keine Standardimplementierung benötigt. Sie deklarieren einfach eine optionale Methode oder eine Variable und sind bereit.

Nachteile

  • Es begrenzt die Fähigkeiten Ihres Protokolls erheblich, da alle konformen Typen kompatibel mit Objective-C sein müssen. Das bedeutet, dass nur Klassen, die von NSObject erben, ein solches Protokoll einhalten können. Keine Strukturen, keine Enums, keine assoziierten 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 viel Boilerplate-Code einführen, wenn Sie oft optionale Methoden aufrufen.

416voto

Antoine Punkte 22558

In Swift 2 und später ist es möglich, Standardimplementierungen eines Protokolls hinzuzufügen. Dies schafft eine neue Möglichkeit für optionale Methoden in Protokollen.

protocol MeinProtokoll {
    func tueEtwasNichtOptionaleMethode()
    func tueEtwasOptionaleMethode()
}

extension MeinProtokoll {
    func tueEtwasOptionaleMethode(){ 
        // Dies bleibt leer 
    }
}

Es ist nicht wirklich eine schöne Möglichkeit, optionale Protokollmethoden zu erstellen, aber es gibt Ihnen 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/

43voto

Raphael Punkte 3746

Hier ist ein konkretes Beispiel mit dem Delegationsmuster.

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 Delegierten auf eine Klasse und implementieren Sie das Protokoll. Beachten Sie, dass die optionale Methode nicht implementiert werden muss.

class AnotherClass: NSObject, MyProtocol
{
    init()
    {
        super.init()

        let myInstance = MyClass()
        myInstance.delegate = self
    }

    func requiredMethod()
    {
    }
}

Ein wichtiger Punkt ist, dass die optionale Methode optional ist und ein "?" beim Aufrufen benötigt. Erwähnen Sie das zweite Fragezeichen.

delegate?.optionalMethod?()

40voto

Eddie.Dou Punkte 459

Da einige Antworten darüber informieren, wie man das optionale Modifier und das @objc Attribut verwendet, um eine optionale Anforderungs-Protokoll zu definieren, werde ich ein Beispiel geben, wie man mit Protokoll-Erweiterungen ein optionales Protokoll definiert.

Der untenstehende Code ist Swift 3.*.

/// Protokoll hat eine leere Standardimplementierung der folgenden Methoden, die optional zu implementieren sind:
/// `cancel()`
protocol Cancelable {

    /// Standardimplementierung ist leer.
    func cancel()
}

extension Cancelable {

    func cancel() {}
}

class Plane: Cancelable {
  //Da cancel() eine Standardimplementierung hat, ist diese optional für die Klasse Plane
}

let plane = Plane()
plane.cancel()
// Gibt *United Airlines kann nicht stornierbar* aus

Bitte beachten Sie, dass Methoden von Protokollerweiterungen nicht von Objective-C-Code aufgerufen werden können, und es ist sogar noch schlimmer, dass das Swift-Team es nicht beheben wird. https://bugs.swift.org/browse/SR-492

36voto

Zag Punkte 819

Die anderen Antworten hier, die das Markieren des Protokolls als "@objc" beinhalten, funktionieren nicht, wenn man swift-spezifische Typen verwendet.

struct Info {
    var height: Int
    var weight: Int
} 

@objc protocol Health {
    func isInfoHealthy(info: Info) -> Bool
} 
//Fehler "Methode kann nicht mit @objc markiert werden, weil der Typ des Parameters nicht in Objective-C dargestellt werden kann"

Um optionale Protokolle zu deklarieren, die gut mit Swift funktionieren, deklarieren Sie die Funktionen als Variablen anstelle von Funktionen.

protocol Health {
    var isInfoHealthy: (Info) -> (Bool)? { get set }
}

Und implementieren Sie das Protokoll dann wie folgt

class Human: Health {
    var isInfoHealthy: (Info) -> (Bool)? = { info in
        if info.weight < 200 && info.height > 72 {
            return true
        }
        return false
    }
    //Oder lassen Sie die Implementierung weg und deklarieren Sie es als:  
    //var isInfoHealthy: (Info) -> (Bool)?
}

Sie können dann "?" verwenden, um zu überprüfen, ob die Funktion implementiert wurde

func returnEntity() -> Health {
    return Human()
}

var anEntity: Health = returnEntity()

var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))? 
//"isHealthy" ist true

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X