97

Is it possible to create an instance of a class by name? Something like:

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

I don't know if this is possible in objective-c but seems like it would be,

Dori
  • 915
  • 1
  • 12
  • 20
Mark
  • 39,551
  • 15
  • 41
  • 47

4 Answers4

220
id object = [[NSClassFromString(@"NameofClass") alloc] init];
Chris McCall
  • 10,317
  • 8
  • 49
  • 80
39

NSClassFromString() runs the risk of mistyping the class name or otherwise using a class that doesn't exist. You won't find out until runtime if you make that error. Instead, if you use the built-in objective-c type of Class to create a variable, then the compiler will verify that the class exists.

For example, in your .h:

@property Class NameOfClass;

and then in your .m:

id object = [[NameOfClass alloc] init];

If you mistyped the class name or if it doesn't exist, you'll get an error at compile time. Also I think this is cleaner code.

Simon Woodside
  • 7,175
  • 5
  • 50
  • 66
  • there you go, buddy. Not entirely sure it's the best answer, as it requires two lines and is less dynamic, but upvoted all the same – Chris McCall May 16 '11 at 15:49
  • 1
    I suppose you could say that it's less dynamic because I used a symbol instead of a string. However, if you know the class that you want when you are writing the code, then it's preferable to use the symbol so as to avoid possible typos. – Simon Woodside Nov 25 '11 at 19:46
  • @sbwoodside: How can this work? I tried it and I got "Undefined symbols for architecture" from the linker. – Lars Schneider Nov 27 '11 at 02:48
  • Change it to [[[self class] alloc] init]; You don't need anything else. – Nick Turner Dec 24 '15 at 19:32
  • The OP's use-case is more than legit - it is the base for any serialization of object hierarchy to file/memory-block/plist/whatever. Many times you DO NOT KNOW in advance which classes you'll need to instantiate. My use-case is the tedious need to "register" gazillion "NSValueTransformer"s and instead of duplicating [NSValueTransformer setValueTransformer:MyTransformerA alloc] init] forName:@"MyTransformerA"]; 40 times - I just scan an NSArray of transformer names - and create/register them from string. – Motti Shneor Oct 27 '18 at 20:25
  • I never said that creating from a string is not legit. However, note that the OP did say "create by name" not "create by string". – Simon Woodside Nov 08 '18 at 23:29
8

If you are working with Objective-C without the NeXTstep (OS X, iOS, GNUstep etc) system or you just think this method is cleaner, then you could utilize the Objective-C language runtime library's API. Under 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 ];

Under the Objective-C (1.0 or unnamed version) you would utilize the following:

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

I haven't tested the 1.0 version, however I have used the 2.0 function in code that is now in production. I personally believe utilizing the 2.0 function is cleaner if available than the NS function as it consumes less space: the length of the name in bytes + 1 ( null terminator ) for the 2.0 API versus the sum of two pointers (isa, cstring), a size_t length (cstring_length), and the length of the string in bytes + 1 for the NeXTSTEP API.

S1LENT WARRIOR
  • 11,704
  • 4
  • 46
  • 60
Mark
  • 1,128
  • 13
  • 21
2
@interface Magic : NSObject
+ (id)createInstanceOfClass:(Class)classe;
@end

@implementation Magic

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

@end

Then to use it:

Car *car = [Magic createInstanceOfClass:[Car class]];
[car engineTurnOn];
Skela
  • 884
  • 10
  • 18