Cocoa: Словарь с ключами перечисления?
Мне нужно создать словарь /hashmap, где
- Ключи перечислены
- Значения - это некоторый подкласс
NSObject
NSDictionary
здесь не будет работать (перечисления не соответствуют NSCopying
).
Возможно, я мог бы использовать CFDictionaryRef
здесь, но я хотел бы знать, есть ли другой способ достичь этого.
Ответы
Ответ 1
Так как перечисления являются целыми числами, вы можете обернуть перечисление в 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];
Ответ 2
С предложением VoidPointer лучше использовать NSValue
для тех случаев, когда перечисления оказываются не целыми (например, когда -fshort-enums
находится в игре, что никогда не должно быть так, как вы, вероятно, нарушаете совместимость с Фонд).
NSValue *value = [NSValue value: &myEnum withObjCType: @encode(enum ETest)];
Это не собирается добавлять много здесь, но дает вам общее "Я хочу использовать < имя метода non-ObjC type > в методе коллекционного класса.
Обратите внимание, что с современными компиляторами вы можете указать enums использовать фиксированный базовый тип. Это означает, что вы можете контролировать, какое хранилище используется для перечисления, но поскольку это решение является общим, оно все равно применяется, даже если вы это знаете.
Ответ 3
Далее распространяется предложение Грэма Ли...
Вы можете использовать objective-c категорию, чтобы добавить метод к NSMutableDictionary, который позволяет вам добавить значение с помощью ключа вашего не NSObject. Это не дает вашему коду свободного от синтаксиса обертывания/развертывания.
Опять же, предполагая
enum ETest { FOO, BAR };
Сначала мы добавляем конструктор convince в 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
Далее мы добавим поддержку "enum 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];
В зависимости от того, сколько вы используете для перечисления для доступа к словарю, это может немного облегчить читаемость вашего кода.
Ответ 4
перечисления не соответствуют NSCopying
Это преуменьшение; перечисления не "согласуются" ни с чем, поскольку они не являются объектами; они являются примитивными значениями C, которые взаимозаменяемы с целыми числами. Это настоящая причина, по которой они не могут использоваться в качестве ключей. Ключи и значения NSDictionary
должны быть объектами. Но поскольку перечисления являются целыми числами, вы можете просто обернуть их в объекты NSNumber
. Вероятно, это самый простой вариант.
Другой вариант, если перечисления смежны от 0 до некоторого числа (т.е. вы не установили никаких значений вручную), это то, что вы можете использовать NSArray
, где индекс представляет значение перечисления ключа. (Любые "отсутствующие" записи должны быть заполнены с помощью NSNull
.)
Ответ 5
Мне также нравится подход к категории, и, вероятно, это будет реализовано и в моем случае. С новыми выражениями "Литералы/бокс", что бы они ни называли, использовало бы @(FOO) заботиться о боксе для вас?
Я думаю, что это было бы, и если это так работает очень прозрачно, явным образом бокс перечисления при использовании его в качестве ключа.