2009-07-27 16 views
23

Necesito crear un diccionario/HashMap donde losCocoa: ¿diccionario con claves enum?

  • llaves son enumeraciones
  • Los valores son algunos subclase de NSObject

NSDictionary no funcionará aquí (enumeraciones que no se ajusten a NSCopying)

Quizás podría utilizar un CFDictionaryRef aquí, pero me gustaría saber si hay alguna otra forma de lograr esto.

Respuesta

29

Dado que las enumeraciones son números enteros, puede ajustar la enumeración en un NSNumber. Cuando se agrega/retreive algo a/desde el mapa, se pasa la enumeración al constructor NSNumber ...

Suponiendo que tienes una enumeración como ...

enum ETest { 
    FOO, BAR 
}; 

Usted puede utilizarlo en un NSDictionary así ...

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

Gracias! Eso funcionó absolutamente genial :) – PlagueHammer

7

enums don't conform to NSCopying

este es un eufemismo; las enumeraciones no se "conforman" a nada ya que no son objetos; son valores primitivos de C que son intercambiables con enteros. Esa es la verdadera razón por la que no pueden usarse como llaves. Las claves y valores de NSDictionary deben ser objetos. Pero como las enumeraciones son números enteros, puedes envolverlas en objetos NSNumber. Esta es probablemente la opción más simple.

Otra opción, si las enumeraciones son contiguas desde 0 hasta un cierto número (es decir, no se establecieron valores manualmente), es que puede usar un NSArray donde el índice representa el valor de la clave enum. (Cualquier "perdidos" entradas tendrían que ser llenado con NSNull.)

28

Con la sugerencia de VoidPointer, puede ser mejor usar NSValue para aquellos momentos en enumeraciones resultan no ser enteros (como cuando -fshort-enums está en juego, que nunca debería ser como probablemente rompería la compatibilidad con Foundation).

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

eso no va a añadir mucho aquí, pero le da al general "Quiero usar < nombre de la no ObjC tipo > en una clase de colección" técnica.

Observe que con compiladores modernos puede decir enums to use a fixed underlying type. Esto significa que puede controlar qué almacenamiento se utiliza para la enumeración, pero como la solución anterior es general, aún se aplica incluso cuando usted sabe esto.

+0

Bien, este es un ajuste mejor, ya que es exactamente lo que NSValue está ahí. ¿NSValue copia el valor apuntado a int el ctor? – VoidPointer

+0

Ok, http://cocoadev.com/forums/comments.php?DiscussionID=1169&page=1 dice que hace una copia :) – VoidPointer

+0

Estoy haciendo lo que sugieres para convertir UIKeyboardType en un 'NSValue', pero estoy obteniendo un error: http://stackoverflow.com/questions/6130315/how-to-add-a-method-to-uitextfield-uitextview/6132880#6132880 – ma11hew28

9

adicional que se extiende sobre la sugerencia de Graham Lee ...

Se podría utilizar un objetivo-c category con el fin de añadir un método para NSMutableDictionary que le permite añadir un valor con una clave de su tipo no NSObject . Esto mantiene su código libre de la sintaxis de envolver/desenvolver.

Una vez más, asumiendo

enum ETest { FOO, BAR }; 

En primer lugar, vamos a añadir un constructor convencer a 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 

A continuación vamos a añadir soporte 'enumeración eTest' a 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 

El ejemplo original se puede transformar a

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

Dependiendo de cuánto use su enumeración para acceder al diccionario, esto puede facilitar bastante la legibilidad de su código.

+0

tiempo para detener la respuesta ping-pong :-). +1 para la categoría NSValue, yo personalmente no usaría la categoría NSMutableDictionary. Me pregunto constantemente por qué no pasé un objeto como clave. Y por qué no podía llamar a '- [NSDictionary objectForETest:]': -/ –

+0

Ojalá hubiera al menos una forma de identificar el "tipo" de alguna/cualquier primitiva, á la '[clase de objeto]'. haría que el proceso de boxeo/desembalaje MUCHO más habitable. –

2

Me gusta el enfoque de categoría también y probablemente implemente esto también en mi caso. Con las nuevas expresiones de Literals/boxeo como se llame, ¿usarías @ (FOO) para encargarte del boxeo?

Creo que sí, y si es así, funciona de manera muy transparente al encasillar explícitamente la enumeración cuando se usa como clave.

Cuestiones relacionadas