objective-c dictionary

objective-c - nsdictionary to object swift



Usar clase como clave en NSDictionary (7)

Estoy escribiendo una "fábrica" ​​contextual que mantendrá un diccionario de objetos convertidores / actuadores que heredan de alguna clase Convertidor. Esta clase tiene un método:

- (Class)classResponsibility

O algo similar, tal que una clase StringConverter implementaría el método como:

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

Luego, para almacenar ese convertidor en el diccionario, esperaba hacer algo como:

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

Pero el compilador se queja de que el tipo "Clase" es un tipo de parámetro no válido para el argumento 2 del método setValue: forKey :. 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.


Acabo de tener una 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 para usarla como clave, pero como mi objeto no sabía cómo copiarse (derivando de NSObject , no tenía copyWithZone en la superclase) se resistió.

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 a todas las allKeys o solo enumerando de otra manera sobre el diccionario.

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


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

Otro requisito para las claves de un diccionario es que admiten copiar (es decir, tienen el método copyWithZone: ¿Los objetos de clase admiten este método? De hecho, sí. La clase NSObject define un método de clase +copyWithZone: cuya documentation explícitamente dice que "te permite usar un objeto de clase como clave para un objeto NSDictionary". Creo que esa es la respuesta a tu pregunta.


Estaba buscando el método setObject: forKey : 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.


Mientras que un objeto Class es una clave perfectamente válida 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 usarlos como claves y / o valores, documented para admitir referencias débiles y punteros arbitrarios.


Puede usar las clases como las claves de NSDictionary esta manera:

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

O incluso así:

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


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ó perfectamente y creo que fue su intención original. Para volver al valor original simplemente llame a nonretainedObjectValue


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