Objective-Cでプロトコルに対応しているかどうかを判定するconformProtocol関数や、メソッドを持っているかどうかを調べるrespondsToSelectorのSwiftでの代替について。
ただし、Objective-Cとは違いSwiftでは静的な型の決定を行っているのでほとんどがコンパイル時に決定でき、動的に判定することはない。というかそのような使い方は避けるべきと考えられる。
ここではごく基本的なものを示す。個別の事情によりまた別の判定方法を使った方がよいことがあるので、それらはいつか実際の事例毎にまとめたい。
まず、準備として1つのプロトコルと4つの親子関係にないクラスを定義する。Swiftでは非形式プロトコルは認められない。また、オプションメソッドも存在しない。Objective-Cのクラスをインポートして使う場合のみにオプションメソッドが使えるので下記のような定義となる。
プロトコルに対応しない2つのクラスと、プロトコルに対応する2つのクラスを定義。それぞれ一方にはプロトコルのオプショナルメソッドと同名のメソッドを定義する。
import Foundation
@objc protocol MyProtocol {
optional func someMethod()
}
class MyClass {
}
class AnotherClass {
func someMethod() {
print("someMethod() in AnotherClass")
}
}
class ClassWithProtocol : MyProtocol {
}
class GoodClass : MyProtocol {
@objc func someMethod() {
print("someMethod() in GoodClass")
}
}
Objective-Cとの違いは同名のメソッドであってもプロトコルを実装していないクラスでは同じメソッド扱いになっていないということ。
SwiftでもSelector("someMethod")のような記法でセレクタを定義できるが、これはObjective-Cのクラスがセレクタを要求する場合にのみ使える。Swiftの側で独自にこのメソッドを呼び出す方法はない。私が見つけられていないだけかもしれないけど。
プロトコルに対応しているかどうかの判定は as? で行う。メソッドを実装しているかどうかは、メソッドを代入する方法で確認できる。
よく使うのはdelegate関数がメソッドを実装しているかどうかだが、それも同様。
let myClass = MyClass()
let anotherClass = AnotherClass()
let classWithProtocol = ClassWithProtocol()
let goodClass = GoodClass()
let array: [AnyObject] = [
myClass, anotherClass, classWithProtocol, goodClass,
]
for object in array {
if let object = object as? MyProtocol {
print("\(String(object.dynamicType)) has MyProtocol.")
}
if let theMethod = object.someMethod {
theMethod()
}
}
var delegate: MyProtocol?
delegate = classWithProtocol
if let theMethod = delegate?.someMethod {
theMethod()
}
delegate = goodClass
if let theMethod = delegate?.someMethod {
theMethod()
}
実行結果の出力は以下。
ClassWithProtocol has MyProtocol.
GoodClass has MyProtocol.
someMethod() in GoodClass
someMethod() in GoodClass