2009-07-27 8 views
23

Ho bisogno di creare un dizionario/hashmap dove iCacao: dizionario con le chiavi enum?

  • Keys sono enumerazioni
  • valori sono un po 'sottoclasse di NSObject

NSDictionary non funziona qui (enumerazioni non sono conformi a NSCopying).

Potrei forse usare uno CFDictionaryRef qui, ma mi piacerebbe sapere se c'è un altro modo per raggiungere questo obiettivo.

risposta

29

Poiché le enumerazioni sono numeri interi, è possibile racchiudere l'enumerazione in un NSNumber. Quando si aggiunge/retreive qualcosa da/per la carta, si passa l'enum al costruttore NSNumber ...

Supponendo che hai un enum come ...

enum ETest { 
    FOO, BAR 
}; 

si può utilizzare in un NSDictionary come questo ...

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

Grazie! Ciò ha funzionato in modo eccellente :) – PlagueHammer

7

enums don't conform to NSCopying

questo è un eufemismo; le enumerazioni non "si conformano" a nulla in quanto non sono oggetti; sono valori C primitivi che sono intercambiabili con interi. Questa è la vera ragione per cui non possono essere usati come chiavi. Le chiavi e i valori di NSDictionary devono essere oggetti. Ma dal momento che le enumerazioni sono numeri interi, puoi semplicemente inserirli negli oggetti NSNumber. Questa è probabilmente l'opzione più semplice.

Un'altra opzione, se le enumerazioni sono contigue da 0 a un numero (ad esempio, non è stato impostato alcun valore manualmente), è possibile utilizzare uno NSArray in cui l'indice rappresenta il valore della chiave enum. (Tutte le voci "mancanti" avrebbero dovuto essere riempito con NSNull.)

28

Con il suggerimento di VoidPointer, può essere meglio utilizzare NSValue per quei tempi, quando le enumerazioni si rivelano non essere numeri interi (come quando -fshort-enums è in gioco, che non dovrebbe mai essere come probabilmente eliminerebbero la compatibilità con Foundation).

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

che non sta andando ad aggiungere molto qui, ma si dà il generale "Voglio usare < nome della non-objC tipo > in una classe collezione" tecnica.

Si noti che con i compilatori moderni è possibile specificare enums to use a fixed underlying type. Ciò significa che è possibile controllare quale spazio di archiviazione viene utilizzato per l'enumerazione, ma poiché la soluzione di cui sopra è generale, si applica anche quando lo si conosce.

+0

Bello, questo è davvero un adattamento migliore in quanto è esattamente ciò che NSValue è lì per. NSValue copia il valore puntato su int the ctor? – VoidPointer

+0

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

+0

Sto facendo ciò che suggerisci di convertire UIKeyboardType in un 'NSValue', ma sto ottenendo un errore: http://stackoverflow.com/questions/6130315/how-to-add-a-method-to-uitextfield-uitextview/6132880#6132880 – ma11hew28

9

Ulteriori estende su suggerimento da Graham Lee ...

Si potrebbe usare un obiettivo-c category al fine di aggiungere un metodo per NSMutableDictionary che permette di aggiungere un valore con una chiave del tipo non NSObject . Ciò mantiene il tuo codice libero dalla sintassi wrapping/unwrapping.

Anche in questo caso, assumendo

enum ETest { FOO, BAR }; 

In primo luogo, stiamo aggiungendo un costruttore convincerlo 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 

successiva dovremo aggiungere il supporto 'enum ETEST' per 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 

L'esempio originale può essere quindi trasformato in

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

A seconda di quanto si utilizza l'enumerazione per accedere al dizionario, ciò potrebbe facilitare un po 'la leggibilità del proprio codice.

+0

tempo di fermare la risposta ping-pong :-). +1 per la categoria NSValue, personalmente non utilizzerei la categoria NSMutableDictionary. Mi stavo continuamente chiedendo perché non ho passato un oggetto come chiave. E perché non potrei chiamare '- [NSDictionary objectForETest:]': -/ –

+0

Vorrei che ci fosse almeno un modo per identificare il "tipo" di alcuni/qualsiasi primitivo, "la classe dell'oggetto". renderebbe il processo di boxing/unboxing molto più vivibile. –

2

Mi piace anche l'approccio di categoria e probabilmente lo implementerò anche nel mio caso. Con le nuove espressioni letterali/boxe, qualunque sia il suo nome, userebbero @ (FOO) prendersi cura della boxe per te?

Penso che sarebbe e se funziona così in modo molto trasparente inscatolando esplicitamente l'enum quando lo si utilizza come chiave.