web-dev-qa-db-fra.com

Utiliser la classe comme clé dans NSDictionary

J'écris une "usine" contextuelle qui maintiendra un dictionnaire d'objets de conversion/agissant hérités d'une classe de convertisseur. Cette classe a une méthode:

- (Class)classResponsibility

Ou quelque chose de similaire, tel qu'une classe StringConverter implémenterait la méthode en tant que:

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

Ensuite, pour stocker ce convertisseur dans le dictionnaire, j'avais espéré pouvoir faire quelque chose comme:

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

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

37
Craig Otis

Vous utilisez setValue:forKey: qui ne prend que NSStrings en tant que clés. vous devriez utiliser setObject:forKey: à la place. Un objet de classe (les pointeurs sur les objets de classe peut être passé sous le 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 ici ), afin qu'elles puissent être utilisées partout où des objets sont utilisés. 

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

98
Skirwan

Votre autre option consiste à utiliser [NSValue valueWithNonretainedObject:yourObjectHere] pour construire la clé à partir de quelque chose d'autre qu'une chaîne. J'ai rencontré un problème similaire et je voulais utiliser un objet CoreData comme clé et quelque chose d'autre comme valeur. Cette méthode NSValue fonctionnait parfaitement et je crois que c’était son intention initiale. Pour revenir à la valeur d'origine, appelez simplement nonretainedObjectValue 

30
Sean

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

4
user23743

Bien qu'un objet Class constitue une clé parfaite dans un NSDictionary, il convient de mentionner NSMapTable, qui est modelé d'après NSDictionary, mais offre plus de flexibilité quant au type d'objets pouvant être utilisés comme clés et/ou valeurs, documenté pour supporter des références faibles et des pointeurs arbitraires.

3
Tom M

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

3
Craig Otis

Je viens de faire face à une situation similaire avec exactement le même message d'erreur:

[tempDictionary setObject:someDictionary forKey:someClass];

Je n’ai fait 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, c’était qu’une copie de someClass était en cours de création pour être utilisée comme clé, mais comme mon objet ne savait pas se copier lui-même (dérivé de NSObject, il n’avait pas de copyWithZone dans la superclasse) il a hésité.

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

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

1
bbrown

Vous pouvez utiliser les classes comme clés de la variable NSDictionary comme ceci:

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

Ou même comme ça:

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