¿Crear instancia de clase de objetivo-c por nombre?

95

¿Es posible crear una instancia de una clase por nombre? Algo como:

NSString* className = @"Car";
id* p = [Magic createClassByName:className];
[p turnOnEngine];

No sé si esto es posible en el objetivo-c, pero parece que lo sería,

marca
fuente

Respuestas:

217
id object = [[NSClassFromString(@"NameofClass") alloc] init];
Chris McCall
fuente
¿Esto no causará fugas?
AntiMoron
38

NSClassFromString()corre el riesgo de escribir mal el nombre de la clase o de usar una clase que no existe. No lo sabrá hasta el tiempo de ejecución si comete ese error. En cambio, si usa el tipo de objetivo-c incorporado Classpara crear una variable, el compilador verificará que la clase existe.

Por ejemplo, en su .h:

@property Class NameOfClass;

y luego en tu .m:

id object = [[NameOfClass alloc] init];

Si escribió mal el nombre de la clase o si no existe, obtendrá un error en el momento de la compilación. También creo que este es un código más limpio.

Simon Woodside
fuente
ahí tienes, amigo. No estoy del todo seguro de que sea la mejor respuesta, ya que requiere dos líneas y es menos dinámico, pero votó a favor de todos modos
Chris McCall
Supongo que se podría decir que es menos dinámico porque usé un símbolo en lugar de una cadena. Sin embargo, si conoce la clase que desea cuando escribe el código, es preferible utilizar el símbolo para evitar posibles errores tipográficos.
Simon Woodside
@sbwoodside: ¿Cómo puede funcionar esto? Lo probé y obtuve "Símbolos indefinidos para arquitectura" del vinculador.
Lars Schneider
Cámbielo a [[[self class] alloc] init]; No necesitas nada más.
Nick Turner
El caso de uso del OP es más que legítimo: es la base para cualquier serialización de la jerarquía de objetos en archivo / bloque de memoria / plist / lo que sea. Muchas veces NO SABE de antemano qué clases necesitará instanciar. Mi caso de uso es la tediosa necesidad de "registrar" gazillion "NSValueTransformer" sy en lugar de duplicar [NSValueTransformer setValueTransformer: MyTransformerA alloc] init] forName: @ "MyTransformerA"]; 40 veces: escaneo un NSArray de nombres de transformadores y los creo / registro desde una cadena.
Motti Shneor
8

Si está trabajando con Objective-C sin el NeXTstep( OS X, iOS, GNUstepetc.) o el sistema que acaba de pensar que este método es más limpio, entonces se podría utilizar la API de biblioteca de tiempo de ejecución de lenguaje Objective-C . Bajo Objective-C 2.0:

#import <objc/runtime.h>
//Declaration in the above named file
id objc_getClass(const char* name);
//Usage
id c = objc_getClass("Object");
[ [ c alloc ] free ];

Bajo Objective-C (1.0 o versión sin nombre) utilizaría lo siguiente:

#import <objc/objc-api.h>
//Declaration within the above named file
Class objc_get_class( const char* name);
//Usage
Class cls = objc_get_class( "Test" );
id obj = class_create_instance( cls );
[ obj free ];

No he probado la 1.0versión, sin embargo, he usado la 2.0función en el código que ahora está en producción. Personalmente, creo que utilizar la 2.0función es más limpio si está disponible que la función NS, ya que consume menos espacio: the length of the name in bytes + 1 ( null terminator )para la API 2.0 versus the sum of two pointers (isa, cstring), a size_t length (cstring_length), y length of the string in bytes + 1para la NeXTSTEPAPI.

marca
fuente
2
@interface Magic : NSObject
+ (id)createInstanceOfClass:(Class)classe;
@end

@implementation Magic

+ (id)createInstanceOfClass:(Class)classe
{
    return [[classe alloc] init];
}

@end

Entonces para usarlo:

Car *car = [Magic createInstanceOfClass:[Car class]];
[car engineTurnOn];
Skela
fuente