Créer une instance de classe objective-c par son nom?

95

Est-il possible de créer une instance d'une classe par son nom? Quelque chose comme:

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

Je ne sais pas si cela est possible dans objective-c mais il semble que ce serait le cas,

marque
la source

Réponses:

217
id object = [[NSClassFromString(@"NameofClass") alloc] init];
Chris McCall
la source
Cela ne causera aucune fuite?
AntiMoron
38

NSClassFromString()court le risque de mal saisir le nom de la classe ou d'utiliser autrement une classe qui n'existe pas. Vous ne saurez pas avant l'exécution si vous faites cette erreur. Au lieu de cela, si vous utilisez le type objectif-c intégré Classpour créer une variable, le compilateur vérifiera que la classe existe.

Par exemple, dans votre .h:

@property Class NameOfClass;

puis dans votre .m:

id object = [[NameOfClass alloc] init];

Si vous avez mal saisi le nom de la classe ou s'il n'existe pas, vous obtiendrez une erreur lors de la compilation. Je pense aussi que c'est un code plus propre.

Simon Woodside
la source
voilà, mon pote. Pas tout à fait sûr que ce soit la meilleure réponse, car elle nécessite deux lignes et est moins dynamique, mais a tout de même voté pour
Chris McCall
Je suppose que vous pourriez dire que c'est moins dynamique parce que j'ai utilisé un symbole au lieu d'une chaîne. Cependant, si vous connaissez la classe souhaitée lorsque vous écrivez le code, il est préférable d'utiliser le symbole afin d'éviter d'éventuelles fautes de frappe.
Simon Woodside
@sbwoodside: Comment cela peut-il fonctionner? Je l'ai essayé et j'ai obtenu "Symboles non définis pour l'architecture" de l'éditeur de liens.
Lars Schneider
Changez-le en [[[self class] alloc] init]; Vous n'avez besoin de rien d'autre.
Nick Turner
Le cas d'utilisation de l'OP est plus que légitime - c'est la base de toute sérialisation de la hiérarchie d'objets en fichier / bloc-mémoire / plist / peu importe. Plusieurs fois, vous NE SAVEZ PAS à l'avance quelles classes vous devrez instancier. Mon cas d'utilisation est le besoin fastidieux de "enregistrer" des millions de "NSValueTransformer" s et au lieu de dupliquer [NSValueTransformer setValueTransformer: MyTransformerA alloc] init] forName: @ "MyTransformerA"]; 40 fois - je scanne juste un NSArray de noms de transformateurs - et les crée / les enregistre à partir d'une chaîne.
Motti Shneor
8

Si vous travaillez avec Objective-C sans NeXTstep( OS X, iOS, GNUstepetc.) ou système vous pensez que cette méthode est plus propre, vous pouvez utiliser l' API de bibliothèque d'exécution de langage Objective-C . Sous 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 ];

Sous Objective-C (version 1.0 ou sans nom), vous utiliseriez ce qui suit:

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

Je n'ai pas testé la 1.0version, cependant j'ai utilisé la 2.0fonction dans le code qui est maintenant en production. Personnellement, je pense que l'utilisation de la 2.0fonction est plus propre si elle est disponible que la fonction NS car elle consomme moins d'espace: the length of the name in bytes + 1 ( null terminator )pour l'API 2.0 par rapport à the sum of two pointers (isa, cstring), a size_t length (cstring_length)et length of the string in bytes + 1pour l' NeXTSTEPAPI.

marque
la source
2
@interface Magic : NSObject
+ (id)createInstanceOfClass:(Class)classe;
@end

@implementation Magic

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

@end

Alors pour l'utiliser:

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