NSDictionaryで複数要素をキーにしようというお話です。
普通の連想配列はキーとバリューで1つの組み合わせになりますが、片方が◯でもう片方が☆のときはこの値みたいなケースの場合は最初から用意されているデータ型では間に合わないことが多いです。
C++ではstd::pair
をキーにしたstd::map
を使えばよいです。Objective-Cではどうやるかわからなかったので調べた結果をまとめます。
ちなみに、複数要素をキーにするようなデータ構造に入れたくなるデータはこんなような感じです。
|Key1|Key2|Value| |::|::|::| |hoge|huga|value1| |hoge|piyo|value2| |hoge|foo|value3| |huga|piyo|value4| |huga|foo|value5| |huga|hoge|value6|
なにはともあれPairクラス
NSObject
を継承したPair
クラスを作って必要なメソッドをオーバーライドして実装しておくことで、NSdictionaryのキーとして使えるようになります。このPair
クラスができてしまえば今回やりたいことはできるようになったも同然です。
Pair.h
@interface Pair : NSObject <NSCopying>
@property (nonatomic, readonly) id first;
@property (nonatomic, readonly) id second;
+ (id) pairWithFirst:(id)f second:(id)s;
- (id) initWithFirst:(id)f second:(id)s;
@end
Pair.m
#import "Pair.h"
@implementation Pair
@synthesize first, second;
+ (id) pairWithFirst:(id)f second:(id)s
{
return [[[self class] alloc] initWithFirst:f second:s];
}
- (id) initWithFirst:(id)f second:(id)s
{
if (self = [super init]) {
first = [f copy];
second = [s copy];
}
return self;
}
- (id) copyWithZone:(NSZone *)zone
{
Pair* copy = [[[self class] alloc] initWithFirst:[self first] second:[self second]];
return copy;
}
- (BOOL) isEqual:(id)other
{
if ([other isKindOfClass:[Pair class]] == NO) { return NO; }
return ([[self first] isEqual:[(Pair*)other first]] && [[self second] isEqual:[(Pair*)other second]]);
}
- (NSUInteger) hash
{
return [[self first] hash] + [[self second] hash];
}
@end
使い方
こんな風に使います。
NSMutableDictionary* dic = [[NSMutableDictionary alloc] init];
[dic setObject:@"A" forKey:[Pair pairWithFirst:@"hoge" second:@"huga"]];
[dic setObject:@"B" forKey:[Pair pairWithFirst:@"foo" second:@"bar"]];
[dic setObject:@"C" forKey:[Pair pairWithFirst:@"hoge" second:@"bar"]];
[dic setObject:@"D" forKey:[Pair pairWithFirst:@"foo" second:@"bar"]];
[dic setObject:@"E" forKey:[Pair pairWithFirst:[NSNumber numberWithInt:10] second:[NSNumber numberWithInt:20]]];
[dic setObject:@"F" forKey:[Pair pairWithFirst:[NSNumber numberWithInt:10] second:[NSNumber numberWithInt:20]]];
同じキーに値を上書きしていることもあって、上のdicには4つ要素が入っていることになります。
かいせつ〜
基本は2つのObjective-Cクラスを受け取って保持しておくクラスです。
NSDictionaryのキーとして使用したいクラスには次のメソッドを正しく実装しておく必要があります。
copyWithZone
isEqual
hash
copyWithZone
NSCopying
プロトコルのcopyWithZone
を実装しておかないとNSDictionaryのキーとしては使えません。キーとして格納する際にコピーが走るから(だと思います)です。
isEqual
連想配列のキーとして使用するには、キー同士が同じであうるかどうかの比較を行うことができなければいけません。そのために必要なメソッドです。
hash
インスタンス毎に一意(異なる)の値を返すように実装しておき、キーが同じであるかの判定に用いられます。先ほど紹介した実装ではペアとなっている要素のhash
の和を返しており厳密には一意の値ではない可能性もあるので、ちゃんとやりたい場合は実装を考える必要があります。