Utworzyć instancję klasy celu-c według nazwy?

95

Czy można utworzyć instancję klasy według nazwy? Coś jak:

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

Nie wiem, czy jest to możliwe w celu-c, ale wygląda na to, że tak byłoby,

znak
źródło

Odpowiedzi:

217
id object = [[NSClassFromString(@"NameofClass") alloc] init];
Chris McCall
źródło
To nie spowoduje żadnych wycieków?
AntiMoron
38

NSClassFromString()stwarza ryzyko błędnego wpisania nazwy klasy lub użycia klasy, która nie istnieje. Nie dowiesz się do czasu uruchomienia, jeśli popełnisz ten błąd. Zamiast tego, jeśli użyjesz wbudowanego typu celu-c Classdo utworzenia zmiennej, kompilator sprawdzi, czy klasa istnieje.

Na przykład w .h:

@property Class NameOfClass;

a następnie w .m:

id object = [[NameOfClass alloc] init];

Jeśli błędnie wpisałeś nazwę klasy lub jeśli nie istnieje, pojawi się błąd w czasie kompilacji. Myślę też, że to bardziej przejrzysty kod.

Simon Woodside
źródło
proszę bardzo, kolego. Nie do końca jestem pewien, że to najlepsza odpowiedź, ponieważ wymaga dwóch wierszy i jest mniej dynamiczna, ale mimo to zyskał uznanie
Chris McCall
Przypuszczam, że można powiedzieć, że jest mniej dynamiczny, ponieważ zamiast łańcucha użyłem symbolu. Jeśli jednak znasz klasę, którą chcesz, pisząc kod, lepiej jest użyć symbolu, aby uniknąć możliwych literówek.
Simon Woodside,
@sbwoodside: Jak to działa? Wypróbowałem to i otrzymałem od linkera "Niezdefiniowane symbole architektury".
Lars Schneider
Zmień to na [[[klasa własna] przydziel] init]; Nie potrzebujesz niczego więcej.
Nick Turner
Przypadek użycia OP jest więcej niż uzasadniony - jest podstawą każdej serializacji hierarchii obiektów do pliku / bloku pamięci / plist / cokolwiek. Wiele razy NIE WIESZ z góry, które klasy będą potrzebne do utworzenia instancji. Moim przypadkiem użycia jest żmudna potrzeba "zarejestrowania" gazillionu "NSValueTransformer" i zamiast duplikowania [NSValueTransformer setValueTransformer: MyTransformerA assign] init] forName: @ "MyTransformerA"]; 40 razy - po prostu skanuję NSArray nazw transformatorów - i tworzę / rejestruję je z łańcucha.
Motti Shneor
8

Jeśli pracujesz z Objective-C bez NeXTstep( OS X, iOS, GNUstepetc) systemu lub po prostu, że ta metoda jest czystsze, a następnie można wykorzystać API Objective-C Language Runtime biblioteki . Pod 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 ];

W ramach Objective-C (1.0 lub wersja nienazwana) użyjesz:

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

Nie testowałem 1.0wersji, ale użyłem 2.0funkcji w kodzie, który jest teraz w produkcji. Osobiście uważam, że korzystanie z 2.0funkcji jest czystsze, jeśli jest dostępna, niż funkcja NS, ponieważ zajmuje mniej miejsca: the length of the name in bytes + 1 ( null terminator )dla API 2.0 w porównaniu z the sum of two pointers (isa, cstring)a size_t length (cstring_length)i length of the string in bytes + 1dla NeXTSTEPAPI.

znak
źródło
2
@interface Magic : NSObject
+ (id)createInstanceOfClass:(Class)classe;
@end

@implementation Magic

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

@end

Następnie, aby go użyć:

Car *car = [Magic createInstanceOfClass:[Car class]];
[car engineTurnOn];
Skela
źródło