2009-04-08 19 views
36

Escribo una "fábrica" ​​contextual que mantendrá un diccionario de objetos convertidores/actuadores que heredan de alguna clase Convertidor. Esta clase tiene un método:Uso de la clase como clave en NSDictionary

- (Class)classResponsibility 

o algo similar, de manera que una clase StringConverter implementaría el método como:

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

Entonces para almacenar ese convertidor en el diccionario, que habían esperado en hacer algo como:

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

Pero el compilador se queja de que el tipo de "clase" es un tipo de parámetro no válido para el argumento 2 de la setValue: forKey: método. Quería evitar establecer la clave como el nombre de la clase ("NSString"), pero si esa es la mejor solución de la que voy a ir con ella.

Respuesta

97

Está utilizando setValue:forKey: que solo toma NSString s como claves. deberías estar usando setObject:forKey: en su lugar. Un objeto de clase (punteros a objetos de clase se pueden pasar como tipo Class) es un objeto Objective-C completo (un objeto de clase es una instancia de su metaclase, y puede usar todos los métodos NSObject en un objeto de clase; lea más sobre meta-classes here), para que puedan ser utilizados en cualquier lugar donde se usen objetos.

Otro requisito para las claves de un diccionario es que apoyar la copia (es decir, tener el método copyWithZone:. ¿Los objetos clase soportan este método? De hecho, lo hace. La clase NSObject define un método clase+copyWithZone:, cuya documentation explícitamente . dice que "le permite utilizar un objeto de la clase como una clave para un objeto NSDictionary" creo que esa es la respuesta a su pregunta

+0

¡Explicación fantástica! –

+23

a partir de XCode 4.5, primero debe emitirlo para no recibir una advertencia: [setObject del diccionario: objeto forKey: (id ) someClass]; vea esta respuesta http://stackoverflow.com/questions/12525324/unable-to-use-class-as-a-key-in-nsdictionary/12525479#12525479 – odyth

+0

¿Conoce la razón técnica por la cual las claves deben cumplir con 'NSCopying ¿? – Drux

4

-setValue:forKey: está documentado para tomar un NSString como el segundo parámetro. Tendrá que usar NSStringFromClass() y NSClassFromString() como adaptadores.

+0

Tienes razón sobre Class ser un objeto –

+0

no hacer la documentación para setValue : forKey: ¿dice que la clave solo tiene que ser un objeto cuando se usa la codificación de clave-valor? De lo contrario, ¿no podría ser cualquier objeto el que implemente el protocolo NSCopying, como NSDictionary? –

3

Estaba buscando el setObjeto: forKey: método en lugar de setValue: forKey :. La firma del método para setObject: forKey: acepta (id) como ambos tipos de parámetros, y se adapta mucho mejor.

+0

@Graham Lee: Las clases implementan NSCopying. Consulte la documentación del método + 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

Vaya, mi error. –

1

que acaba de tener un cultivo situación similar con el mismo mensaje de error exacto:.

[tempDictionary setObject:someDictionary forKey:someClass]; 

Todo lo que hice fue implementar el protocolo NSCopying en someClass:

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

Creo que lo que sucedía era que se estaba haciendo una copia de someClass con el fin de ser utilizado como la clave, pero ya que mi objetivo no sabía cómo copiarse (derivando de NSObject no tenía un copyWithZone en la superclase) se detuvo.

Una cosa que he encontrado con mi enfoque es que se usa un objeto como clave. A menos que ya tenga el objeto instanciado, estoy constantemente llamando al allKeys o simplemente enumerando el diccionario.

[Después de escribir esto, veo que desea almacenar la clase como tal. Voy a dejar esto porque habría ahorrado mucho tiempo si hubiera encontrado mi respuesta cuando estaba buscando SO.No encontré nada así luego.]

+0

Acabo de tropezar con este problema hoy, pero tenía esta otra clase funcionando sin problemas durante muchos meses y no entendía por qué. ¿Alguien sabría una razón? "Modelo" es una clase personalizada derivada solo de NSObject (no compatible con NSCoding) 'static NSMutableDictionary * singletonInstances; @implementation SingletonModel + (void) inicializar { \t singletonInstances = [[NSMutableDictionary alloc] initWithCapacity: 10]; } + (void) setSharedModel: (Modelo *) UN MODELO { \t si: { \t \t [singletonInstances setObject: UN MODELO forKey: [clase UN MODELO]] ([singletonInstances objectForKey [clase UN MODELO]]!); \t} } ' – nobre

+0

@nobre: ​​los objetos de clase se pueden usar como claves. ver http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html # // apple_ref/doc/uid/20000050-copyWithZone_ – user102008

+0

Esta publicación contiene una buena discusión sobre los peligros de implementar 'NSCopying': http://robnapier.net/blog/implementing-nscopying-439 – penfold

30

Su otra opción es usar [NSValue valueWithNonretainedObject:yourObjectHere] para construir la clave a partir de algo que no sea una cadena. Me encontré con un problema similar y quería usar un objeto CoreData como clave y algo más como valor. Este método NSValue funcionó perfecto y creo que fue su intención original. Para volver al valor original simplemente llame al nonretainedObjectValue

+1

Muy interesante, gracias por ese. Desafortunadamente, ya he bajado por el otro camino, pero en el futuro recordaré intentarlo. –

+0

Alternativa muy impresionante en lugar de implementar copyWithZone en un millón de clases para mí :) – Will

3

Mientras que un objeto Class hace una tecla perfectamente buena en un NSDictionary, vale la pena mencionar NSMapTable, que está modelado según NSDictionary, pero proporciona más flexibilidad en cuanto a qué tipo de objetos son adecuados para usar como claves y/o valores, documented para admitir referencias débiles y punteros arbitrarios.

+0

¡Gran solución iOS6! – Meda

+0

Esta debería ser la nueva solución preferida iOS 6 y más. –

+0

¿Se puede usar weak para la clave de clase con NSMapTable? ¿Alguna posibilidad de que muera en silencio? – Andy

0

Puede utilizar clases como NSDictionary 's claves como este:

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

ni aun como este:

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