Cocoa: Dictionary with enum keys?

23,004

Solution 1

Since enums are integers, you can wrap the enum in an NSNumber. When you add/retreive something to/from the map, you pass the enum to the NSNumber constructor...

Assuming you've got an enum like...

enum ETest {
    FOO, BAR
};

You can use it in an NSDictionary like this...

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

Solution 2

Further extending on the suggestion from Graham Lee...

You could use an objective-c category in order to add a method to NSMutableDictionary that allows you to add a value with a key of your non NSObject type. This keeps your code free from the wrapping/unwrapping syntax.

Again, assuming

enum ETest { FOO, BAR };

First, we're adding a convince constructor to 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

Next we'll add 'enum ETest' support to 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

The original Example can thus be transformed to

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

Depending on how much you use your enum to access the dictionary this may ease readability of your code quite a bit.

Solution 3

enums don't conform to NSCopying

This is an understatement; enums do not "conform" to anything as they are not objects; they are primitive C values which are interchangeable with integers. That's the real reason why they can't be used as keys. The keys and values of NSDictionary need to be objects. But since enums are integers, you can just wrap them into NSNumber objects. This is probably the simplest option.

Another option, if the enums are contiguous from 0 up to some number (i.e. you didn't set any values manually), is that you can use an NSArray where the index represents the key enum's value. (Any "missing" entries would have to be filled with NSNull.)

Solution 4

The category approach has its own uses, but the newer boxed expressions (e.g. @(FOO)) should take care of type conversion for you. It works very transparently by explicitly boxing the enum when using it as a key.

Share:
23,004
Debajit
Author by

Debajit

Updated on June 21, 2020

Comments

  • Debajit
    Debajit almost 4 years

    I need to create a dictionary/hashmap where the

    • Keys are enums
    • Values are some subclass of NSObject

    NSDictionary won't work here (enums don't conform to NSCopying).

    I could perhaps use a CFDictionaryRef here, but I'd like to know if is there any other way to achieve this.