web-dev-qa-db-ja.com

Swiftプロトコルでオプションのメソッドを定義する方法

それはSwiftで可能ですか?そうでない場合、それを行うための回避策はありますか?

321
Selvin

1.デフォルトの実装を使用する(推奨).

protocol MyProtocol {
    func doSomething()
}

extension MyProtocol {
    func doSomething() {
        /* return a default value or just leave empty */
    }
}

struct MyStruct: MyProtocol {
    /* no compile error */
}

利点

  • いいえObjective-Cランタイムは関係ありません(まあ、少なくとも明示的には関係ありません)。つまり、構造体、列挙型、および非NSObjectname__クラスをそれに準拠させることができます。また、これはあなたが強力なジェネリック医薬品システムを利用できることを意味します。

  • そのようなプロトコルに適合する型に遭遇したとき、あなたはいつでもすべての要件が満たされていることを確認することができます。それは常に具体的な実装かデフォルトの実装のどちらかです。これが「インタフェース」または「規約」が他の言語でどのように振る舞うかです。

短所

  • _ Voidname__以外の要件の場合、妥当なデフォルト値を持つ必要があります。これは常に可能なわけではありません。しかし、この問題に遭遇したとき、それはそのような要件が本当にデフォルト実装を持たないはずであるか、あなたがあなたがAPI設計の間にミスをしたということを意味します。

  • デフォルトの実装とまったく実装されていないを区別することはできません。少なくとも特別な戻り値でこの問題に対処しない限り。次の例を見てください。

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

    あなたが単にtruename__を返すデフォルトの実装を提供するならば - それは一目で大丈夫です。今、次の疑似コードを考えてみましょう:

    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 */
            }
        }
    }
    

    そのような最適化を実装する方法はありません - あなたのデリゲートがメソッドを実装しているかどうかを知ることはできません。

    この問題を解決するにはいくつかの方法がありますが(オプションのクロージャを使用したり、異なるオペレーションに異なるデリゲートオブジェクトを使用したりします)、その例は問題を明確に示しています。


2. @objc optionalを使用します。

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

class MyClass: NSObject, MyProtocol {
    /* no compile error */
}

利点

  • デフォルトの実装は必要ありません。あなたはただオプションのメソッドや変数を宣言するだけで準備完了です。

短所

  • すべての適合型がObjective-C互換であることを要求することで、プロトコルの機能を厳しく制限します。つまり、NSObjectname__から継承したクラスだけがそのようなプロトコルに準拠できます。構造体も列挙型も関連型もありません。

  • オプションのメソッドが実装されているかどうかを常にチェックする必要があります。オプションのメソッドを頻繁に呼び出す場合は、これによって多くの定型句が導入されることがあります。

416
akashivskyy

Swift 2以降では、プロトコルのデフォルト実装を追加することが可能です。これはプロトコルのオプションメソッドの新しい方法を作成します。

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

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

これはオプションのプロトコルメソッドを作成するのにはあまり良い方法ではありませんが、プロトコルコールバックの中で構造体を使用することができます。

私はここに簡単な要約を書いた: https://www.avanderlee.com/Swift-2-0/optional-protocol-methods/

382
Antoine

オプションの要求プロトコルを定義するために オプションの修飾子 @objc attribute を使用する方法についていくつかの回答がありますので、プロトコル拡張定義オプションのプロトコルの使用方法についてサンプルを示します。

以下のコードは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*

Objective-Cコードからプロトコル拡張メソッドを呼び出すことはできません。さらに悪いことに、Swiftチームはそれを修正できません。 https://bugs.Swift.org/browse/SR-492

36
Eddie.Dou

プロトコルを "@objc"としてマークすることを含むここでの他の答えは、Swift特有の型を使うときはうまくいきません。

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"

Swiftでうまく機能するオプションのプロトコルを宣言するには、関数をfuncの代わりに変数として宣言します。

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

そして次のようにプロトコルを実装します

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)?
}

あなたはそれから "?"を使うことができます機能が実装されているかどうかをチェックする

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

var anEntity: Health = returnEntity()

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

これは委任パターンの具体例です。

プロトコルを設定します。

@objc protocol MyProtocol:class
{
    func requiredMethod()
    optional func optionalMethod()
}

class MyClass: NSObject
{
    weak var delegate:MyProtocol?

    func callDelegate()
    {
        delegate?.requiredMethod()
        delegate?.optionalMethod?()
    }
}

デリゲートをクラスに設定してプロトコルを実装します。オプションのメソッドを実装する必要がないことを確認してください。

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

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

    func requiredMethod()
    {
    }
}

1つの重要なことは、オプションの方法はオプションであり、「?」が必要なことです。電話するとき。 2番目の疑問符に言及してください。

delegate?.optionalMethod?()
32
Raphael

In - Swift 3.0

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

それはあなたの時間を節約します。

31
Gautam Sareriya
  • 各メソッドの前にoptionalキーワードを追加する必要があります。
  • ただし、これが機能するには、 プロトコルに@objc属性を付ける必要があります。
  • これはさらに、このプロトコルはクラスには適用できるが構造には適用できないことを意味します。
26
Mehul Parmar

プロトコル継承による純粋なSwiftアプローチ:

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

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

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

アントワーヌの答えの力学を説明するために:

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

尋ねる前に どうやって あなたはオプションのプロトコルメソッドを実装することができます、あなたは尋ねるべきです なぜ 実装する必要があります。

Swiftプロトコルを古典的なオブジェクト指向プログラミングで Interface と考えるならば、オプションのメソッドはあまり意味がありません、そしておそらくより良い解決策はデフォルト実装を作成するか、プロトコルをプロトコルのセットに分けることでしょうプロトコル内のメソッドの可能な組み合わせを表す(おそらくそれらの間のいくつかの継承関係を持つ)。

詳細については、 https://useyourloaf.com/blog/Swift-optional-protocol-methods/ を参照してください。

8
Eran Marom

元の質問のトピックからは少し離れていますが、それはAntoineの考えから成り立っているので、誰かに役立つかもしれないと思いました。

プロトコル拡張を持つ構造体では、計算プロパティをオプションにすることもできます。

プロトコルのプロパティをオプションにすることができます

protocol SomeProtocol {
    var required: String { get }
    var optional: String? { get }
}

プロトコル拡張でダミーの計算プロパティを実装する

extension SomeProtocol {
    var optional: String? { return nil }
}

そして今、あなたはオプションのプロパティを実装しているかしていない構造体を使用することができます

struct ConformsWithoutOptional {
    let required: String
}

struct ConformsWithOptional {
    let required: String
    let optional: String?
}

私は自分のブログで Swiftプロトコルのオプションのプロパティの使い方 を書いています - /は、Swift 2のリリースで状況が変わった場合に備えて更新していきます。

7
matthewpalmer

もしあなたが純粋なSwiftでそれをしたいのなら、最善の方法は、例えば struct のようなSwift型をSwift型で返すならば、特にデフォルト実装を提供することです。

例:

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

それならあなたはすべてを定義せずにプロトコルを実装することができます func

3
p1ckl3

これはSwift Classesのみの非常に単純な例です。構造体や列挙型の例ではありません。プロトコル方法はオプションであり、2つのレベルのオプションの連鎖が再生中にあることに注意してください。また、プロトコルを採用しているクラスは、その宣言に@objc属性を必要とします。

@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

オプションおよび必須のデリゲートメソッドを作成する方法。

@objc protocol InterViewDelegate:class {

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

}