読者です 読者をやめる 読者になる 読者になる

絶品ゆどうふのタレ

ふと気づいたことを綴るだけのメモ

独自のオブジェクトをcontainsObjectで判別できるようにする。

objective-c tips

これもはまったのでメモ。
NSArrayにはcontainsObject:っていう、引数で渡したオブジェクト・インスタンスと同じものが格納されているかを判定してくれる超ベンリなメソッドがある。
が、これはNSStringとかを使ってる分にはサクサク便利だが、独自オブジェクトを配列にぽんぽこ入れといた場合なんかには、何故か正常に判定できないことがある。


特に自分がはまったパターンは、先述の記事みたいに一度保存した独自オブジェクトを取り出したとき、保持データは完全に一致しているにも関わらず必ずNOが返ってきて困った。

これの原因は、containsObject:の動作が、格納しているオブジェクトのisEqual:メソッドを順番に呼び出して行く、という仕組みになっていることに起因する。


このisEqual:メソッド、Appleのマニュアルによると

NSObjectにおけるisEqual:のデフォルトの実装は、単純にポインタの等価性を確認するだけです。

Cocoa Fundamentals Guide: イントロスペクション

っていう、超能天気実装なので、「保持データは同じだけど別のインスタンス」って場合には同一オブジェクトとはみなしてくれない。


まぁ、たしかに厳密な話をするとそうかも知れないけど。。。空気嫁!って感じです。
そんなわけで、独自オブジェクトなんかを作った場合にcontainsObject:したいなんて時には*1、新しいオブジェクト側でisEqual:をオーバーライドしとくべき。


例えばこんな感じ*2

- (BOOL) isEqual:(id)other {
  if (other == self) {
    return YES; // ポインタ的に一致、ってのは必ず。
  } else if ([other isKindOfClass:[self class]) {
    if ( [aString isEqualToString:[other valueForKey:@"aString"]]
      && [bString isEqualToString:[other valueForKey:@"bString"]]
    ) {
      return YES;
    }
  }
  return NO;
}

こうしとけば、自由に「同一のオブジェクトとみなす」条件が決められるので、containsObject:も同一だと判定してくれるようになる。


と言う感じでひと段落。

*1:良くあると思うんだ。このパターンって

*2:ちなみにサンプルは適当なので、hashも上書きしろとか、厳密さはどう取るとか色々あるので、その辺まじめにやるならドキュメントを熟読してください