wake-up-neo.com

Wie definieren Sie optionale Methoden im Swift-Protokoll?

Ist es in Swift möglich? Wenn nicht, gibt es eine Problemumgehung?

303
Selvin

Da es einige Antworten zur Verwendung des optionalen Modifizierers und des @objc-Attributs gibt, um das optionale Anforderungsprotokoll zu definieren, gebe ich ein Beispiel zur Verwendung von Protokollerweiterungen, wobei das optionale Protokoll definiert wird.

Der folgende Code lautet 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 mit Objective-C-Code aufgerufen werden können. Schlimmer ist, dass das Swift-Team das Problem nicht beheben kann. https://bugs.Swift.org/browse/SR-492

29
Eddie.Dou

Wenn Sie optionale Methoden verwenden möchten, müssen Sie müssen Ihr Protokoll mit dem Attribut @objc markieren:

@objc protocol MyProtocol {

    @objc optional func doSomething()

}

class MyClass : MyProtocol {

    // no error

}

Ansonsten verhalten sich die Swift-Protokolle wie Schnittstellen in anderen OO -Sprachen, in denen alle in Schnittstellen definierten Methoden implementiert werden müssen.

404
akashivskyy

Ab Swift 2 können Standard-Implementierungen eines Protokolls hinzugefügt werden. Dies schafft eine neue Möglichkeit für optionale Methoden in Protokollen. 

protocol MyProtocol {
    func doSomethingNonOptionalMethod()
    func doSomethingOptionalMethod()
}

extension MyProtocol {
    func doSomethingOptionalMethod(){ 
        // leaving this empty 
    }
}

Es ist keine wirklich schöne Möglichkeit, optionale Protokollmethoden zu erstellen, aber Sie haben 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/

370
Antoine

Die anderen Antworten, die das Protokoll als "@objc" kennzeichnen, funktionieren nicht, wenn Swift-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 gut mit Swift funktionieren, deklarieren Sie die Funktionen als Variablen anstelle von Funktionen. 

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

Und dann implementieren Sie 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
32
Zag

In Swift 3.0

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}

Es wird Ihre Zeit sparen.

31
Gautam Sareriya

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 Delegaten 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()
    {
    }
}

Eine wichtige Sache ist, dass die optionale Methode optional ist und ein "?" beim anrufen. Erwähnen Sie das zweite Fragezeichen.

delegate?.optionalMethod?()
31
Raphael
  • Sie müssen vor jeder Methode das optional-Schlüsselwort hinzufügen. 
  • Beachten Sie jedoch, dass Ihr Protokoll mit dem @objc-Attribut gekennzeichnet sein muss, damit dies funktioniert.
  • Dies impliziert weiter, dass dieses Protokoll auf Klassen anwendbar wäre, nicht auf Strukturen.
26
Mehul Parmar

Ein reiner Swift-Ansatz mit Protokollvererbung:

//Required methods
protocol MyProtocol {
    func foo()
}

//Optional methods
protocol MyExtendedProtocol: MyProtocol {
    func bar()
}

class MyClass {
    var delegate: MyProtocol
    func myMethod() {
        (delegate as? MyExtendedProtocol).bar()
    }
}
12
guenis

Um die Mechanik der Antwort von Antoine 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"
10
eLillie

Ich denke das bevor du fragst wie Sie können eine optionale Protokollmethode implementieren, sollten Sie fragen warum Sie sollten einen implementieren. 

Wenn wir Swift-Protokolle als Interface in der klassischen objektorientierten Programmierung betrachten, sind optionale Methoden nicht sinnvoll, und eine bessere Lösung wäre die Erstellung einer Standardimplementierung oder die Trennung des Protokolls in eine Reihe von Protokollen (vielleicht 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/ . Dort erhalten Sie einen hervorragenden Überblick.

6
Eran Marom

Ein wenig von der ursprünglichen Frage abweichendes Thema, aber es baut auf Antoines Idee auf, und ich dachte, es könnte jemandem helfen. 

Sie können berechnete Eigenschaften 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 }
}

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 auch in meinem Blog [] beschrieben, wie optionale Eigenschaften in den Swift-Protokollen gemacht werden, die ich auf dem neuesten Stand halte, falls sich die Swift-2-Versionen ändern.

6
matthewpalmer

wenn Sie es in pure Swift tun möchten, ist es am besten, eine Standardimplementierung bereitzustellen, wenn Sie einen Swift-Typ wie struct 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 kannst du das Protokoll implementieren, ohne jeden func zu definieren

3
p1ckl3

Hier ist ein sehr einfaches Beispiel für NUR Swift-Klassen und nicht für Strukturen oder Aufzählungen. Beachten Sie, dass die Protokollmethode optional ist. Sie hat zwei Ebenen der optionalen Verkettung. Auch die Klasse, die das Protokoll verwendet, benötigt das @objc-Attribut 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!)
    }

 }
3
Blessing Lopes

So erstellen Sie optionale und erforderliche Delegierungsmethoden.

@objc protocol InterViewDelegate:class {

    @objc optional func optfunc()  //    This is optional
    func requiredfunc()//     This is required 

}