Criar instância de classe object-c por nome?

95

É possível criar uma instância de uma classe por nome? Algo como:

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

Não sei se isso é possível em c objetivo, mas parece que seria,

Marca
fonte

Respostas:

217
id object = [[NSClassFromString(@"NameofClass") alloc] init];
Chris McCall
fonte
Isso não vai causar nenhum vazamento?
AntiMoron
38

NSClassFromString()corre o risco de digitar incorretamente o nome da classe ou usar uma classe que não existe. Você não descobrirá até o tempo de execução se cometer esse erro. Em vez disso, se você usar o tipo objetivo-c interno de Classpara criar uma variável, o compilador verificará se a classe existe.

Por exemplo, em seu .h:

@property Class NameOfClass;

e então em seu .m:

id object = [[NameOfClass alloc] init];

Se você digitou incorretamente o nome da classe ou se ele não existe, você receberá um erro em tempo de compilação. Também acho que este é um código mais limpo.

Simon Woodside
fonte
lá vai você, amigo. Não tenho certeza absoluta de que é a melhor resposta, pois requer duas linhas e é menos dinâmica, mas votou positivamente da mesma forma
Chris McCall
Suponho que você poderia dizer que é menos dinâmico porque usei um símbolo em vez de uma string. No entanto, se você conhece a classe que deseja ao escrever o código, é preferível usar o símbolo para evitar possíveis erros de digitação.
Simon Woodside
@sbwoodside: Como pode funcionar? Eu tentei e obtive "Símbolos indefinidos para arquitetura" do linker.
Lars Schneider
Altere para [[[self class] alloc] init]; Você não precisa de mais nada.
Nick Turner
O caso de uso do OP é mais do que legítimo - é a base para qualquer serialização da hierarquia de objetos para arquivo / bloco de memória / plist / qualquer coisa. Muitas vezes você NÃO SABE de antemão quais classes você precisará instanciar. Meu caso de uso é a necessidade tediosa de "registrar" zilhões de "NSValueTransformer" se em vez de duplicar [NSValueTransformer setValueTransformer: MyTransformerA alloc] init] forName: @ "MyTransformerA"]; 40 vezes - acabo de escanear um NSArray de nomes de transformadores - e crio / registro-os a partir da string.
Motti Shneor
8

Se você está trabalhando com Objective-C sem a NeXTstep( OS X, iOS, GNUstepsistema etc) ou você só acho que este método é mais limpo, em seguida, você poderia utilizar a API da biblioteca de tempo de execução linguagem Objective-C . Sob 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 ];

No Objective-C (1.0 ou versão sem nome), você utilizaria o seguinte:

#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 ];

Não testei a 1.0versão, mas usei a 2.0função em código que agora está em produção. Pessoalmente, acredito que usar a 2.0função é mais limpo, se disponível, do que a função NS, pois consome menos espaço: the length of the name in bytes + 1 ( null terminator )para a API 2.0 versus the sum of two pointers (isa, cstring), a size_t length (cstring_length), e length of the string in bytes + 1para a NeXTSTEPAPI.

Marca
fonte
2
@interface Magic : NSObject
+ (id)createInstanceOfClass:(Class)classe;
@end

@implementation Magic

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

@end

Então, para usá-lo:

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