Swift で NSObject を継承したオブジェクトの等価演算子を拡張する
Swift プログラミング
Swift で JSValue どうしを等価比較できるようにと思って == 演算子を実装してみたところ、ジェネリック関数で正しく動いてくれませんでした。
今回のように NSObject を継承したオブジェクトでは、等価演算子を直接実装するのではなく isEqual メソッドをオーバーライドすることで実現する必要があります。
たとえば JavaScriptCore
の JSType型
を Swift で等価演算子を使って比較できるようにしたいと思ったときに、素直に Equatable
に準拠させようとすると、次のエラーでビルドできませんでした。
Redundant conformance of 'JSValue' to protocol 'Equatable'
これは JSValue
が継承している NSObject
が既に Equatable
に準拠しているためなのでしょう。
だからといって、そのまま次のように定義しても、うまく使えない場合がでてきます。
func == (lhs:JSValue, rhs:JSValue) -> Bool {
}
直接 JSValue
の値どうしを比較すればちゃんと動くのですけど、たとえば Swift の XCTestAssertEqual
関数のように Equatable
を想定したジェネリック関数の場合だと、本来の Equatable に準拠している NSObject
用の ==
演算子が呼び出されてしまい、上の実装は無視されてしまいます。
NSObject を継承したオブジェクトで == を実装する
どうしたものかとつい考えてしまったんですが、そういえば Objective-C のときには当たり前にやっていた方法で、Swift で ==
演算子を使った比較を実装することができました。
やり方は Objective-C ではごく当たり前な方法で、JSValue で isEqual:
メソッドをオーバーライドしてあげます。
extension JSValue {
override public func isEqual(object: AnyObject?) -> Bool {
if let value = object as? JSValue {
return self.isEqualToValue(value)
}
else {
return super.isEqual(object)
}
}
public func isEqualToValue(object: JSValue) -> Bool {
return JSValueIsEqual(self.context.JSGlobalContextRef, self.JSValueRef, object.JSValueRef, nil)
}
}
そうすると、JSValue
用に ==
を実装しなくても、JSValue
同士の比較が行われて NSObject
用の ==
が実行されたときに、この JSValue
でオーバーライドした isEqual:
メソッドが呼び出されます。
これで Equatable
を想定したジェネリック関数に JSValue
の値を渡したときにも、正しく等価判定ができるようになりました。