web-dev-qa-db-ja.com

ココア:列挙型キーのある辞書?

辞書/ハッシュマップを作成する必要があります。

  • キーは列挙型です
  • 値はNSObjectのサブクラスです

NSDictionaryはここでは機能しません(列挙型はNSCopyingに準拠していません)。

ここでCFDictionaryRefを使用することもできますが、これを実現する他の方法があるかどうかを知りたいと思います。

24
PlagueHammer

列挙型は整数であるため、列挙型をNSNumberでラップできます。マップに何かを追加/マップから取得するときは、列挙型をNSNumberコンストラクターに渡します...

あなたが次のような列挙型を持っていると仮定します...

enum ETest {
    FOO, BAR
};

このようなNSDictionaryで使用できます...

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject: @"Foo!" forKey:[NSNumber numberWithInt: FOO]];
NSLog(@"getting value for FOO -> %@", 
      [dict objectForKey: [NSNumber numberWithInt: FOO]]);  
[dict release]; 
30
VoidPointer

VoidPointerの提案により、列挙型が整数ではないことが判明した場合(-fshort-enumsが機能している場合など、Foundationとの互換性を損なう可能性があるため、決して使用しないでください)にはNSValueを使用する方がよい場合があります。

NSValue *value = [NSValue value: &myEnum withObjCType: @encode(enum ETest)];

ここではあまり追加しませんが、一般的な「コレクションクラスで<非ObjCタイプの名前>を使用したい」というテクニックを紹介します。

最新のコンパイラでは、 固定の基になる型を使用する列挙型 と言うことができることに注意してください。これは、列挙型に使用されるストレージを制御できることを意味しますが、上記のソリューションは一般的であるため、これを知っている場合でも適用されます。

28
user23743

グラハム・リーからの提案をさらに拡張します...

NSMutableDictionaryにメソッドを追加して、NSObject以外のタイプのキーを使用して値を追加できるようにするために、objective-c category を使用できます。これにより、コードがラッピング/アンラッピング構文から解放されます。

繰り返しますが、

enum ETest { FOO, BAR };

まず、NSValueに説得力のあるコンストラクターを追加します。

@interface NSValue (valueWithETest)
+(NSValue*) valueWithETest:(enum ETest)etest; 
@end

@implementation NSValue (valueWithETest)
+(NSValue*) valueWithETest:(enum ETest)etest
{
    return [NSValue value: &etest withObjCType: @encode(enum ETest)];
}
@end

次に、「列挙型ETest」サポートをNSMutableDictionaryに追加します

@interface NSMutableDictionary (objectForETest)
-(void) setObject:(id)anObject forETest:(enum ETest)key;
-(id) objectForETest:(enum ETest)key;
@end

@implementation NSMutableDictionary (objectForETest)
-(void) setObject:(id)anObject forETest:(enum ETest)key
{
    [self setObject: anObject forKey:[NSValue valueWithETest:key]];
}
-(id) objectForETest:(enum ETest)key
{
    return [self objectForKey:[NSValue valueWithETest:key]];
}
@end

したがって、元の例は次のように変換できます。

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject: @"Bar!" forETest:BAR];
NSLog(@"getting value Bar -> %@", [dict objectForETest: BAR]);      
[dict release];

列挙型を使用して辞書にアクセスする量によっては、コードがかなり読みやすくなる場合があります。

9
VoidPointer

列挙型がNSCopyingに準拠していません

これは控えめな表現です。列挙型はオブジェクトではないため、何にも「適合」しません。それらは整数と交換可能なプリミティブC値です。それが、キーとして使用できない本当の理由です。 NSDictionaryのキーと値はオブジェクトである必要があります。ただし、列挙型は整数であるため、列挙型をNSNumberオブジェクトにラップするだけで済みます。これはおそらく最も簡単なオプションです。

別のオプションは、列挙型が0から特定の数まで連続している場合(つまり、手動で値を設定しなかった場合)、インデックスがキー列挙型の値を表すNSArrayを使用できることです。 (「欠落している」エントリはすべてNSNullで埋める必要があります。)

7
newacct

私はカテゴリーアプローチも好きで、おそらく私の場合にもこれを実装するでしょう。新しいリテラル/ボクシング式と呼ばれるものでは、@(FOO)を使用してボクシングを処理しますか?

キーとして使用するときに列挙型を明示的にボックス化することで、非常に透過的に機能すると思います。

2
FrostyL