2009-04-08 6 views
36

J'écris une "fabrique" contextuelle qui maintiendra un dictionnaire d'objets convertisseurs/agissant qui héritent d'une classe de Convertisseur. Cette classe a une méthode:Utiliser la classe comme clé dans NSDictionary

- (Class)classResponsibility 

Ou quelque chose de semblable, telle qu'une classe StringConverter mettrait en œuvre la méthode que:

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

Ensuite, pour stocker ce convertisseur dans le dictionnaire, je l'avais espéré à faire quelque chose comme:

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

Mais le compilateur se plaint que le type "classe" est un type de paramètre non valide pour l'argument 2 de la setValue: forKey: méthode. Je voulais éviter de définir la clé comme le nom de classe ("NSString"), mais si c'est la meilleure solution que je vais aller avec.

Répondre

97

Vous utilisez setValue:forKey: qui prend seulement NSString s comme clés. vous devriez utiliser setObject:forKey: à la place. Un objet de classe (les pointeurs vers des objets de classe peuvent être passés en tant que type Class) est un objet Objective-C à part entière (un objet de classe est une instance de sa méta-classe et vous pouvez utiliser toutes les méthodes NSObject sur un objet de classe; en savoir plus sur les méta-classes here), afin qu'ils puissent être utilisés partout où des objets sont utilisés.

Une autre exigence pour les clés d'un dictionnaire est qu'ils soutiennent la copie (c.-à-ont la méthode copyWithZone:. Est-ce que les objets de classe prennent en charge cette méthode? En fait, il le fait. La classe NSObject définit une méthode de classe +copyWithZone:, dont documentation explicitement . dit qu'il « vous permet d'utiliser un objet de classe comme une clé pour un objet NSDictionary » Je pense que c'est la réponse à votre question

+0

Explication fantastique! –

+23

à partir de XCode 4.5, vous devez d'abord le lancer pour ne pas recevoir d'avertissement: [dictionary setObject: object forKey: (id ) someClass]; voir cette réponse http://stackoverflow.com/questions/12525324/unable-to-use-class-as-a-key-in-nsdictionary/12525479#12525479 – odyth

+0

Savez-vous la raison technique pour laquelle les clés doivent se conformer à 'NSCopying '? – Drux

4

-setValue:forKey: est documentée pour prendre un NSString comme second paramètre. Vous devrez utiliser NSStringFromClass() et NSClassFromString() comme adaptateurs.

+0

Vous avez raison sur la Classe d'un objet –

+0

Ne pas les docs pour setValue : forKey: dire que la clé doit seulement être un objet lors de l'utilisation du codage de valeur-clé? Sinon, ne pourrait-il pas être un objet qui implémente le protocole NSCopying, comme NSDictionary? –

3

Je cherchais le setObject: forKey: méthode au lieu de setValue: forKey :. La signature de la méthode pour setObject: forKey: accepte (id) comme les deux types de paramètres, et est beaucoup mieux adapté.

+0

@Graham Lee: Les classes implémentent NSCopying. Voir la documentation de la méthode + copyWithZone: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/doc/uid/20000050-copyWithZone_ – user102008

+0

Oups, mon erreur. –

1

Je viens d'avoir une culture de situation semblable avec exactement le même message d'erreur:.

[tempDictionary setObject:someDictionary forKey:someClass]; 

Tout ce que je ne faisais que mettre en œuvre le protocole NSCopying dans someClass:

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

Je pense que ce qui se passait était qu'une copie someClass était faite pour être utilisé comme clé, mais depuis mon objet ne savais pas comment copier lui-même (dérivant de NSObject il n'avait pas un copyWithZone dans la superclasse) il a rechigné.

Une chose que j'ai trouvée avec mon approche est qu'elle utilise un objet comme clé. À moins que l'objet ne soit déjà instancié, j'appelle constamment allKeys ou j'énumère autrement le dictionnaire.

[Après avoir écrit ceci, je vois que vous voulez stocker la classe en tant que telle que la clé. Je laisse ceci dehors parce que j'aurais sauvé beaucoup de temps si j'avais trouvé ma réponse quand je cherchais SO.Je n'ai rien trouvé de tel alors.]

+0

Je viens de trébucher sur ce problème aujourd'hui, mais cette autre classe fonctionnait parfaitement depuis plusieurs mois et je ne comprenais pas pourquoi. Quelqu'un connaîtrait-il une raison? "Model" est une classe personnalisée dérivant de NSObject uniquement (pas compatible NSCoding) 'static NSMutableDictionary * singletonInstances; @implementation SingletonModel + (void) initialiser { \t singletonInstances = [[NSMutableDictionary alloc] initWithCapacity: 10]; } + (void) setSharedModel: (Modèle *) AMODEL { \t si: { \t \t [singletonInstances setObject: AMODEL forKey: [Classe AMODEL]] ([singletonInstances objectForKey [classe AMODEL]]!); \t} } ' – nobre

+0

@nobre: ​​les objets de classe peuvent être utilisés comme clés. voir http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html #// apple_ref/doc/uid/20000050-copyWithZone_ – user102008

+0

Ce poste a une bonne discussion sur les pièges de l'implémentation 'NSCopying': http://robnapier.net/blog/implementing-nscopying-439 – penfold

30

Votre autre option consiste à utiliser [NSValue valueWithNonretainedObject:yourObjectHere] pour construire la clé à partir d'autre chose qu'une chaîne. J'ai rencontré un problème similaire et je voulais utiliser un objet CoreData comme clé et autre chose comme valeur. Cette méthode NSValue a fonctionné parfaitement et je crois que c'était l'intention originale. Pour revenir à la valeur d'origine il suffit d'appeler nonretainedObjectValue

+1

Très intéressant, merci pour cette. J'ai malheureusement déjà pris l'autre chemin, mais dans le futur je me souviendrai d'essayer. –

+0

Très impressionnant alternative plutôt que d'implémenter copyWithZone dans un million de classe pour moi :) – Will

3

Alors qu'un objet Class constitue une clé parfaite dans un NSDictionary, il vaut la peine de mentionner NSMapTable, qui est basé sur NSDictionary, mais offre plus de flexibilité quant aux types d'objets qui conviennent pour utilisation en tant que clés et/ou valeurs, documented pour prendre en charge les références faibles et les pointeurs arbitraires.

+0

Grande solution iOS6! – Meda

+0

Cela devrait être la nouvelle solution préférée iOS 6 et au-delà. –

+0

Pouvez-vous utiliser faible pour la clé de classe avec NSMapTable? Toute possibilité de mourir en silence? – Andy

0

Vous pouvez utiliser les classes comme NSDictionary « s clés comme celui-ci:

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

Ou encore comme ceci:

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