2009-04-08 18 views
36

Sto scrivendo una "fabbrica" ​​contestuale che manterrà un dizionario di oggetti convertitore/recitazione che ereditano da qualche classe del convertitore. Questa classe ha un metodo:Utilizzo della classe come chiave in NSDictionary

- (Class)classResponsibility 

o qualcosa di simile, in modo tale che una classe StringConverter sarebbe implementare il metodo come:

- (Class)classResponsibility { 
    return [NSString class]; 
} 

Poi per memorizzare che convertitore nel dizionario, avevo sperato di fare qualcosa come:

[converters setValue:stringConverter forKey:[stringConverter classResponsibility]]; 

Ma il compilatore si lamenta che il tipo "Class" è un tipo di parametro valido per l'argomento 2 della setValue: Forkey: metodo. Volevo evitare di impostare la chiave come nome della classe ("NSString"), ma se questa è la soluzione migliore di quella che farò.

risposta

97

Si sta usando setValue:forKey: che richiede solo NSString s come chiavi. dovresti usare setObject:forKey: invece. Un oggetto di classe (i puntatori agli oggetti di classe possono essere passati come tipo Class) è un oggetto Objective-C completo (un oggetto di classe è un'istanza della sua meta-classe, e puoi usare tutti i metodi NSObject su un oggetto di classe; leggi di più sulle meta-classi here), in modo che possano essere utilizzate ovunque vengano utilizzati oggetti.

Un altro requisito per le chiavi di un dizionario è che supporta la copia (cioè hanno il metodo copyWithZone:. Fare oggetti di classe supportano questo metodo? In realtà, lo fa. La classe NSObject definisce un metodo classe+copyWithZone:, la cui documentation esplicitamente . dice che "consente di utilizzare un oggetto di classe come chiave per un oggetto NSDictionary" penso che sia la risposta alla tua domanda

+0

Fantastica spiegazione! –

+23

come da XCode 4.5, è necessario prima lanciarlo per non ricevere un avviso: [dizionario setObject: oggetto forKey: (id ) someClass]; vedi questa risposta http://stackoverflow.com/questions/12525324/unable-to-use-class-as-a-key-in-nsdictionary/12525479#12525479 – odyth

+0

Conosci il motivo tecnico per cui le chiavi devono essere conformi a 'NSCopying '? – Drux

4

-setValue:forKey: è documentato per prendere uno NSString come secondo parametro. Dovrai utilizzare NSStringFromClass() e NSClassFromString() come adattatori.

+0

Hai ragione su Class essere un oggetto –

+0

Non la documentazione per setValue : forKey: dire che la chiave deve essere solo un oggetto quando si utilizza la codifica del valore-chiave? Altrimenti, non potrebbe essere un qualsiasi oggetto che implementa il protocollo NSCopying, come NSDictionary? –

3

Stavo cercando il setObject: forKey: metodo anziché setValue: forKey :. La firma del metodo per setObject: forKey: accetta (id) come entrambi i tipi di parametro ed è molto più adatta.

+0

@Graham Lee: le classi implementano NSCopying. Vedi la documentazione del + copyWithZone: metodo: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/doc/uid/20000050-copyWithZone_ – user102008

+0

Oops, mio ​​errore. –

1

ho appena avuto una situazione simile raccolto con l'esatto messaggio di errore stesso.

[tempDictionary setObject:someDictionary forKey:someClass]; 

Tutto quello che ho fatto è stato l'attuazione del protocollo NSCopying in someClass:

- (id)copyWithZone:(NSZone *)zone 
{ 
    id copy = [[[self class] allocWithZone:zone] init]; 
    [copy setId:[self id]]; 
    [copy setTitle:[self title]]; 
    return copy; 
} 

Penso che ciò che stava accadendo era che una copia di someClass era stato fatto al fine di essere utilizzato come chiave, ma dal momento che il mio scopo non sapevo come copiare se stesso (derivando da NSObject non aveva un copyWithZone nella superclasse) ha esitato.

Una cosa che ho trovato con il mio approccio è che usa un oggetto come chiave. A meno che non abbia già istanziato l'oggetto, chiamerò costantemente allKeys o semplicemente enumerando il dizionario.

[Dopo aver scritto questo, vedo che si desidera memorizzare la classe come tale. Lascio questo fuori là perché avrei risparmiato un sacco di tempo se avessi trovato la mia risposta quando stavo cercando SO.Quindi non ho trovato nulla di simile.]

+0

Oggi mi sono imbattuto in questo problema, ma questa classe ha funzionato in modo impeccabile per molti mesi e non ho capito il perché. Qualcuno saprebbe una ragione? "Modello" è una classe personalizzata derivata solo da NSObject (non compatibile con NSCoding) 'static NSMutableDictionary * singletonInstances; @implementation SingletonModel + (void) {inizializzare \t singletonInstances = [[NSMutableDictionary alloc] initWithCapacity: 10]; } + (void) setSharedModel: (Modello *) Amodel { \t se: { \t \t [singletonInstances setObject: Amodel Forkey: [class Amodel]] ([singletonInstances objectForKey [classe Amodel]]!); \t} } ' – nobre

+0

@nobre: ​​gli oggetti di classe possono essere utilizzati come chiavi. vedere http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html # // apple_ref/doc/uid/20000050-copyWithZone_ – user102008

+0

Questo post ha una buona discussione sulle insidie ​​dell'implementazione di 'NSCopying': http://robnapier.net/blog/implementing-nscopying-439 – penfold

30

L'altra opzione è quella di utilizzare [NSValue valueWithNonretainedObject:yourObjectHere] per costruire la chiave da qualcosa di diverso da una stringa. Mi sono imbattuto in un problema simile e volevo utilizzare un oggetto CoreData come chiave e qualcos'altro come valore. Questo metodo NSValue ha funzionato perfettamente e credo fosse il suo intento originale. Per tornare al valore originale basta chiamare nonretainedObjectValue

+1

Molto interessante, grazie per quella. Sfortunatamente ho già seguito l'altra strada, ma in futuro mi ricorderò di provare. –

+0

Un'alternativa davvero fantastica piuttosto che implementare copyWithZone in una classe di milioni per me :) – Will

3

Mentre un oggetto Class fa una chiave perfettamente valida in un NSDictionary, vale la pena menzionare NSMapTable, che viene modellato dopo NSDictionary, ma offre una maggiore flessibilità su quale tipo di oggetti sono adatti da utilizzare come chiavi e/o valori, documented per supportare riferimenti deboli e puntatori arbitrari.

+0

Ottima soluzione iOS6! – Meda

+0

Questa dovrebbe essere la nuova soluzione preferita iOS 6 e oltre. –

+0

È possibile utilizzare debole per la chiave di classe con NSMapTable? Qualche possibilità morirà silenziosamente? – Andy

0

È possibile utilizzare le classi come NSDictionary 's chiavi come questo:

@{ 
    (id)[MyClass1 class] : @1, 
    (id)[MyClass2 class] : @2, 
    (id)[MyClass3 class] : @3, 
    (id)[MyClass4 class] : @4, 
}; 

e neppure come questo:

@{ 
    MyClass1.self : @1, 
    MyClass2.self : @2, 
    MyClass3.self : @3, 
    MyClass4.self : @4, 
}; 
Problemi correlati