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


359

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


1
やっと自分の答えを更新するように動機付けられました。あなたはそれを受け入れることを再考することができます。
akashivskyy

@akashivskyy利用可能なすべてのオプションとその長所と短所をよりよく示すため、私はあなたの答えを受け入れます。
セルビン

回答:


505

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ランタイムは含まれません(少なくとも、明示的にはありません)。つまり、構造体、列挙型、非NSObjectクラスをそれに準拠させることができます。また、これは強力なジェネリックシステムを利用できることを意味します。

  • このようなプロトコルに準拠するタイプに遭遇し場合、すべての要件が満たされていることを常に確認できます。それは常に具体的な実装かデフォルトの実装です。これは、「インターフェース」または「契約」が他の言語でどのように動作するかです。

短所

  • 非のためVoidの要件は、合理的なデフォルト値持つ必要が常に可能ではありません、。ただし、この問題が発生した場合は、そのような要件に実際のデフォルト実装がないか、API設計中にミスを犯したことを意味します。

  • デフォルトの実装と実装なしを区別することはできません。少なくとも、特別な戻り値でその問題に対処する必要があります。次の例を考えてみます。

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

    返すだけのデフォルト実装を提供する場合true、一見すると問題ありません。ここで、次の疑似コードを考えます。

    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互換であることを要求することにより、プロトコルの機能を大幅に制限します。つまり、継承元のクラスだけがそのNSObjectようなプロトコルに準拠できます。構造体、列挙型、関連する型はありません。

  • オプションのメソッドが実装されているかどうかは、オプションで呼び出しを行うか、準拠する型が実装しているかどうかを常にチェックする必要があります。オプションのメソッドを頻繁に呼び出す場合、これは多くのボイラープレートを導入する可能性があります。


17
拡張機能を使用して、オプションのメソッドの改善された方法をすばやく確認する(下)
Daniel Kanaan 2015年

1
また、インスタンス内のオプションのプロトコルメソッドのサポートをどのようにテストしますか?respondsToSelector
devios1

3
これをしないでください!Swiftは、理由によりプロトコルでオプションのメソッドをサポートしていません。
fpg1503 2016年

2
このメソッドは、パラメーターのオプションをサポートしていません。つまり、それを行うことはできませんoptional func doSomething(param: Int?)
SoftDesigner 2016年

4
確かに、この答えは今日、数年後の現在、本質的に間違っています。今日のSwiftでは、デフォルト実装の拡張機能を追加するだけです。これはSwiftの基本的な側面です。(以下の最近のすべての答えが示すように)。今日では、objcフラグを追加するのは間違っています。
Fattie

394

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/


2
これは、おそらくSwiftで行う最もクリーンな方法です。Swift 2.0以前では機能しません。
Entalpi

13
@MattQuiros実際にはプロトコル定義で関数を宣言する必要があることがわかりました。そうでない場合、no-op拡張関数はプロトコルに準拠するクラスでオーバーライドされません。
Ian Pearce

3
@IanPearceは正しいです。これは仕様によるもののようです。WWDCでの「プロトコル指向プログラミング」トーク(408)では、メインプロトコルのメソッドが、適合タイプに提供される「カスタマイズポイント」であると述べています。必要なカスタマイズポイントは、拡張機能の定義を受け取りません。オプションのポイントが行います。一般にカスタマイズすべきではないプロトコルのメソッドは、拡張で完全に宣言/定義されており、動的タイプに特にキャストしてコンフォーマーのカスタム実装が必要であることを示す場合を除いて、適合タイプをカスタマイズできません。
Matthias

4
@FranklinYuこれを行うことはできますが、実際には不要な「スロー」を使用してAPIデザインを汚染しています。「マイクロプロトコル」のアイデアが好きです。たとえば、各メソッドが1つのプロトコルを確認し、次のことを確認できます。オブジェクトがプロトコルである場合
Darko

3
@Antoineプロトコルの関数は定義によりパブリックであるため、拡張機能の関数をパブリックにする必要があると思います。プロトコルがモジュールの外で使用される場合、ソリューションは機能しません。
Vadim Eisenberg 2017年

39

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

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


1
よくやった!Objective-Cランタイムを関与させることなく問題を解決するため、これは正解です。
Lukas

本当にいい!
tania_S

迅速なドキュメントから-「拡張機能によって提供されるデフォルト実装のプロトコル要件は、オプションのプロトコル要件とは異なります。準拠するタイプは独自の実装を提供する必要はありませんが、デフォルト実装の要件は、オプションのチェーンなしで呼び出すことができます。」
Mec Os

34

プロトコルを「@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

3
このソリューションでは、どのプロトコル機能からでも自分にアクセスできません。これにより、問題が発生する場合があります。
ジョージグリーン

1
@GeorgeGreen自分にアクセスできます。関数変数を遅延としてマークし、クロージャー内のキャプチャリストを使用します。
ザグ2014

@objcを使用できるのは、クラス、プロトコル、メソッド、およびプロパティのみです。@ objcプロトコルメソッドの定義でEnumパラメータを使用している場合、失敗します。
khunshan 2014年

1
@khunshan、このメソッドは@ objcとマークされているものを何も必要としません。
Zag、

enumがswiftとobjcの間で使用できないというトピックに関する情報です。他のステートメントのforは@objcキーワードでブリッジできます。
khunshan 2015

34

委任パターンの具体的な例を次に示します。

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

@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?()

2
これが正解です。シンプルでクリーンで説明的。
エシェロン

私は同意します、この答えは短く、甘く、要点にまっすぐです。ありがとう!
LuAndre 2016年

31

ではスウィフト3.0

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

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


6
なぜ@objc今すべてのメンバーに必要なのか?
DevAndArtist 2017年

@Gautam Sareriya:このアプローチまたは空の拡張メソッドを作成するのが一番良いと思いますか?
Eonist 2017年

requiredフラグ付きのメソッドを試しましたが、エラーが発生しました:required「init」宣言でのみ使用できます。
ベン

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

マイナーな修正(編集するには小さすぎます!)ですが、「@ optional」ではなく「optional」にする必要があります
Ali Beadle

5
また、プロトコルを実装するクラスを、プロトコル@objcだけでなくとしてマークする必要があります。
Johnathon Sullinger、2015

13

プロトコルを継承する純粋な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

アントワーヌの答えのメカニズムを説明するには:

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"

8

オプションのプロトコルメソッドを実装する方法を尋ねる前に、なぜそれを実装する必要があるのを尋ねるべきだと思います。

迅速なプロトコルを クラシックオブジェクト指向プログラミングインターフェイス、オプションのメソッドはあまり意味がなく、おそらくデフォルトの実装を作成するか、プロトコルを一連のプロトコルに分離すること(おそらくいくつかの継承関係がある)プロトコル間のメソッドの可能な組み合わせを表すため。

詳細については、https://useyourloaf.com/blog/swift-optional-protocol-methods/を参照してください。これにより、この問題に関する優れた概要が提供されます。


この。これは、SOLIDソフトウェア開発原則「I」原則に準拠しています。
エリック

7

元の質問から少し外れたトピックですが、それはアントワーヌのアイデアから成り立ち、私はそれが誰かを助けるかもしれないと思いました。

また、プロトコル拡張を使用した構造体の計算プロパティをオプションにすることもできます。

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

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プロトコルでオプションのプロパティを実行する方法についても説明しました。Swift2のリリースを通じて変更があった場合に備えて、このプロパティを更新します。


5

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

@objc protocol InterViewDelegate:class {

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

}

3

以下は、Swiftクラスのみの非常に単純な例であり、構造体や列挙型の例ではありません。プロトコルメソッドはオプションであり、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!)
    }

 }

プレイ中のオプションの2つのレベル」の部分、つまりこれについてもう少し説明してくださいdelegate?.indexDidChange?(index!)
Unheilig

2
次のような非オプションのメソッドを持つようにプロトコルを記述した場合: protocol CollectionOfDataDelegate{ func indexDidChange(index: Int) } 疑問符なしでそれを呼び出しdelegate?.indexDidChange(index!) ます: プロトコルのメソッドにオプションの要件を設定すると、それに準拠するタイプはそのメソッドを実装しない可能性がありますなので、?は実装の確認に使用され、ない場合はプログラムがクラッシュしません。@Unheilig
Blessing Lopes、

weak var delegate : CollectionOfDataDelegate?(弱い参照を保証しますか?)
アルフィー・ハンセン2015年

@BlessingLopes delegate?使用法の説明を回答に追加できますか?その情報は、将来、他の人のために本当にそこに属すべきです。私はこれに賛成したいのですが、その情報は本当に答えの中にあるはずです。
Johnathon Sullinger、2015

3

あなたが純粋に迅速にそれをしたいのであれば、あなたがSwiftタイプの構造体のようなSwiftタイプを返す場合、デフォルトの実装のparticullaryを提供することが最善の方法です

例:

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

その後、すべての関数を定義せずにプロトコルを実装できます


2

Swiftプロトコルでオプションのメソッドを作成する方法は2つあります。

1-最初のオプションは、@ objc属性を使用してプロトコルをマークすることです。これは、クラスによってのみ採用できることを意味しますが、次のように個々のメソッドをオプションとしてマークすることを意味します。

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

2-より迅速な方法:このオプションの方が優れています。このように、何もしないオプションメソッドのデフォルト実装を記述します。

protocol MyProtocol {
    func optionalMethod()
    func notOptionalMethod()
}

extension MyProtocol {

    func optionalMethod() {
        //this is a empty implementation to allow this method to be optional
    }
}

Swiftには拡張機能と呼ばれる機能があり、オプションにするメソッドのデフォルト実装を提供できます。


0

1つのオプションは、それらをオプションの関数変数として格納することです。

struct MyAwesomeStruct {
    var myWonderfulFunction : Optional<(Int) -> Int> = nil
}

let squareCalculator =
    MyAwesomeStruct(myWonderfulFunction: { input in return input * input })
let thisShouldBeFour = squareCalculator.myWonderfulFunction!(2)

-1

プロトコルで関数を定義し、そのプロトコルの拡張を作成してから、オプションとして使用する関数の空の実装を作成します。


-2

Optional Protocol迅速に定義するには、宣言の@objc前にキーワードを使用し、そのプロトコル内で/ 宣言を使用する必要があります。以下は、プロトコルのオプションプロパティのサンプルです。Protocolattributemethod

@objc protocol Protocol {

  @objc optional var name:String?

}

class MyClass: Protocol {

   // No error

}

2
これは質問に答えるかもしれませんが、この答えが問題の解決にどのように役立つかについていくつかの説明を追加することをお勧めします。詳細を知るには、どのようにすればよい答えを書くことができますかをお読みください。
Roshana Pitigala

-23

入れ@optionalメソッドやプロパティの前に。


コンパイラエラー: 'optional'属性は、@ objcプロトコルのメンバーにのみ適用できます。
Selvin

3
@optionalも正しいキーワードではありません。それはoptionalであり、属性使用してクラスプロトコルを宣言する必要があり@objcます。
Johnathon Sullinger、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.