A Day In The Life

とあるプログラマの備忘録

Swiftのプロトコルに@objcなしでオプショナルメソッドを定義する

Swift で プロトコルを定義するときにオプショナルなメソッドを定義したい時ってありますよね。そんなときに使える小技です。

@objcを指定するよく知られた方法

Swift のプロトコルObjective-C と違ってオプショナルメソッドを定義することができません。そこで一番お手軽な方法としてはプロトコル宣言するとき、先頭に@objcをつけて Objective-C 互換にしてしまうことです。こんな感じです。

@objc protocol Hoge {
  func foo()
  func bar()
  // オプショナルなメソッド
  optional func buzz()
}

使うときは以下のようになります。

class Fuga: Hoge {
  func foo() {
    // 実装
  }
  func bar() {
    // 実装
  }
}
// メソッドの呼び出し
let fuga = Fuga()
fuga.foo()
fuga.bar()
fuga.buzz?()

Objc 互換にする方法には以下のデメリットがあります。

  • 構造体(struct)や列挙型(enum)で使えない
  • メソッドを呼び出すときに「?」をつけないといけない
  • ジェネリクスが使えない

@objcなしで実装する方法

@objcなしでもっと Swift っぽくオプショナルメソッドを定義する方法があります。それがextensionを使ってメソッドのデフォルト実装を定義する方法です。以下のようになります。

protocol Hoge {
  func foo()
  func bar()
  // オプショナルなメソッド
  func buzz()
}

public extension MIDIMessageListener {
  // デフォルト実装を定義する
  func buzz() {
    print("buzz")
  }
}

使い方は以下のようになります。

class Fuga: Hoge {
  func foo() {
    // 実装
  }
  func bar() {
    // 実装
  }
}
// メソッドの呼び出し
let fuga = Fuga()
fuga.foo()
fuga.bar()
fuga.buzz()

メソッドの呼び出し方も他のメソッドの呼び出し方と同じです。合わせて構造体や列挙型にも適応できます。