Какао: словарь с ключами перечисления?

Мне нужно создать словарь / хэш-карту, где

  • Ключи - это перечисления
  • Значения являются подклассом NSObject

NSDictionary здесь работать не будет (перечисления не соответствуют NSCopying).

Возможно, я мог бы использовать здесь CFDictionaryRef, но я хотел бы знать, есть ли другой способ добиться этого.


person Debajit    schedule 27.07.2009    source источник


Ответы (5)


Поскольку перечисления являются целыми числами, вы можете заключить перечисление в 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]; 
person VoidPointer    schedule 27.07.2009

С предложением VoidPointer может быть лучше использовать NSValue в тех случаях, когда перечисления оказываются не целыми (например, когда -fshort-enums находится в игре, что никогда не должно быть, поскольку вы, вероятно, нарушите совместимость с Foundation).

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

Это не собирается здесь ничего добавлять, но дает вам общую технику «Я хочу использовать‹ имя не-ObjC-типа ›в классе коллекции».

Обратите внимание, что с помощью современных компиляторов вы можете указать перечислениям использовать фиксированный базовый тип. Это означает, что вы можете контролировать, какое хранилище используется для перечисления, но, поскольку приведенное выше решение является общим, оно все еще применяется, даже если вы это знаете.

person Community    schedule 27.07.2009
comment
Хорошо, это действительно лучше, поскольку NSValue существует именно для этого. Копирует ли NSValue значение, указанное в ctor? - person VoidPointer; 27.07.2009
comment
Хорошо, cocoadev.com/forums/comments.php?DiscussionID=1169&page=1 говорит, что делает копию :) - person VoidPointer; 27.07.2009
comment
Я делаю то, что вы предлагаете, для преобразования UIKeyboardType в NSValue, но получаю сообщение об ошибке: stackoverflow.com/questions/6130315/ - person ma11hew28; 26.05.2011
comment
@MattDiPasquale - вы заменяете @encode (enum ETest) на @encode (enum UIKeyboardType), верно? Тип не только определяется флагами компилятора, но также может варьироваться (особенно между подписанными и беззнаковыми) в зависимости от диапазона самих значений перечисления. - person DougW; 20.07.2011

Продолжая предложение Грэма Ли ...

Вы можете использовать objective-c category, чтобы добавить в NSMutableDictionary метод, который позволяет вам добавлять значение с ключом вашего типа, отличного от NSObject. Это избавляет ваш код от синтаксиса упаковки / развертывания.

Опять же, предполагая

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

Затем мы добавим поддержку 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];

В зависимости от того, насколько часто вы используете перечисление для доступа к словарю, это может немного облегчить читаемость вашего кода.

person VoidPointer    schedule 27.07.2009
comment
Пора перестать отвечать пинг-понг :-). +1 для категории NSValue, хотя лично я бы не стал использовать категорию NSMutableDictionary. Я постоянно задавался вопросом, почему я не передал объект в качестве ключа. И почему я не мог позвонить -[NSDictionary objectForETest:]: - / - person ; 28.07.2009
comment
Хотелось бы, чтобы был хотя бы способ определить тип какого-либо примитива, ля [object class]. это сделало бы процесс упаковки / распаковки НАМНОГО более жизнеспособным. - person Alex Gray; 02.10.2012

перечисления не соответствуют NSCopying

Это преуменьшение; перечисления ни с чем не «согласуются», поскольку они не являются объектами; это примитивные значения C, которые взаимозаменяемы с целыми числами. Это настоящая причина, по которой их нельзя использовать в качестве ключей. Ключи и значения NSDictionary должны быть объектами. Но поскольку перечисления являются целыми числами, вы можете просто заключить их в NSNumber объекты. Это, наверное, самый простой вариант.

Другой вариант, если перечисления непрерывны от 0 до некоторого числа (т.е. вы не устанавливали никаких значений вручную), заключается в том, что вы можете использовать NSArray, где индекс представляет значение ключевого перечисления. (Любые «отсутствующие» записи должны быть заполнены NSNull.)

person newacct    schedule 27.07.2009

У категориального подхода есть свои собственные применения, но новые упакованные выражения (например, @(FOO)) должны позаботиться о преобразовании типов за вас. Он работает очень прозрачно, явно упаковывая перечисление при использовании его в качестве ключа.

person FrostyL    schedule 09.11.2012