Instanz der Objective-C-Klasse nach Namen erstellen?

95

Ist es möglich, eine Instanz einer Klasse nach Namen zu erstellen? Etwas wie:

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

Ich weiß nicht, ob dies in Ziel-C möglich ist, aber es scheint so,

Kennzeichen
quelle

Antworten:

217
id object = [[NSClassFromString(@"NameofClass") alloc] init];
Chris McCall
quelle
Dies wird keine Undichtigkeiten verursachen?
AntiMoron
38

NSClassFromString()Es besteht die Gefahr, dass der Klassenname falsch eingegeben oder eine nicht vorhandene Klasse verwendet wird. Sie werden erst zur Laufzeit herausfinden, ob Sie diesen Fehler machen. Wenn Sie stattdessen den integrierten Ziel-c-Typ von verwenden Class, um eine Variable zu erstellen, überprüft der Compiler, ob die Klasse vorhanden ist.

Zum Beispiel in Ihrem .h:

@property Class NameOfClass;

und dann in Ihrem .m:

id object = [[NameOfClass alloc] init];

Wenn Sie den Klassennamen falsch eingegeben haben oder nicht vorhanden sind, wird beim Kompilieren eine Fehlermeldung angezeigt. Ich denke auch, dass dies sauberer Code ist.

Simon Woodside
quelle
Los geht's, Kumpel. Nicht ganz sicher, ob es die beste Antwort ist, da es zwei Zeilen erfordert und weniger dynamisch ist, aber trotzdem positiv bewertet wird
Chris McCall
Ich nehme an, man könnte sagen, dass es weniger dynamisch ist, weil ich ein Symbol anstelle einer Zeichenfolge verwendet habe. Wenn Sie jedoch die gewünschte Klasse kennen, wenn Sie den Code schreiben, ist es vorzuziehen, das Symbol zu verwenden, um mögliche Tippfehler zu vermeiden.
Simon Woodside
@sbwoodside: Wie kann das funktionieren? Ich habe es versucht und vom Linker "Undefinierte Symbole für Architektur" erhalten.
Lars Schneider
Ändern Sie es in [[[Selbstklasse] zuweisen] init]; Du brauchst nichts anderes.
Nick Turner
Der Anwendungsfall des OP ist mehr als legitim - er ist die Basis für jede Serialisierung der Objekthierarchie in Datei / Speicherblock / Liste / was auch immer. Oft wissen Sie NICHT im Voraus, welche Klassen Sie instanziieren müssen. Mein Anwendungsfall ist die mühsame Notwendigkeit, Millionen von "NSValueTransformer" zu "registrieren" und statt [NSValueTransformer setValueTransformer: MyTransformerA alloc] init zu duplizieren] forName: @ "MyTransformerA"]; 40 Mal - ich scanne nur ein NSArray mit Transformatornamen - und erstelle / registriere sie aus einem String.
Motti Shneor
8

Wenn Sie mit Objective-C ohne arbeiten NeXTstep( OS X, iOS, GNUstepetc) System oder Sie denken , nur diese Methode sauberer ist, dann könnte man die nutzen Sprache Objective-C - Laufzeitbibliothek API . Unter 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 ];

Unter Objective-C (1.0 oder unbenannte Version) würden Sie Folgendes verwenden:

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

Ich habe die 1.0Version nicht getestet , aber ich habe die 2.0Funktion in Code verwendet, der jetzt in Produktion ist. Ich persönlich glaube, dass die Verwendung der 2.0Funktion sauberer ist, wenn sie verfügbar ist als die NS-Funktion, da sie weniger Speicherplatz beansprucht: the length of the name in bytes + 1 ( null terminator )für die 2.0-API im Vergleich zu the sum of two pointers (isa, cstring)a size_t length (cstring_length)und length of the string in bytes + 1für die NeXTSTEPAPI.

Kennzeichen
quelle
2
@interface Magic : NSObject
+ (id)createInstanceOfClass:(Class)classe;
@end

@implementation Magic

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

@end

Dann, um es zu benutzen:

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