Создать экземпляр класса objective-c по имени?

95

Можно ли создать экземпляр класса по имени? Что-то вроде:

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

Я не знаю, возможно ли это в objective-c, но похоже, что это было бы,

отметка
источник

Ответы:

217
id object = [[NSClassFromString(@"NameofClass") alloc] init];
Крис МакКолл
источник
Это не вызовет утечек?
AntiMoron
38

NSClassFromString()рискует ошибиться при вводе имени класса или иным образом использовать несуществующий класс. Вы не узнаете до времени выполнения, если сделаете эту ошибку. Вместо этого, если вы используете встроенный тип objective-c Classдля создания переменной, компилятор проверит, существует ли класс.

Например, в вашем .h:

@property Class NameOfClass;

а затем в вашем .m:

id object = [[NameOfClass alloc] init];

Если вы неправильно набрали имя класса или он не существует, вы получите ошибку во время компиляции. Также я думаю, что это более чистый код.

Саймон Вудсайд
источник
вот так, приятель. Не совсем уверен, что это лучший ответ, так как он требует двух строк и менее динамичен, но все равно получил голосование
Крис МакКолл,
Я полагаю, вы могли бы сказать, что он менее динамичен, потому что я использовал символ вместо строки. Однако, если вы знаете, какой класс вам нужен при написании кода, тогда предпочтительнее использовать символ, чтобы избежать возможных опечаток.
Саймон Вудсайд
@sbwoodside: Как это может работать? Я попробовал и получил от компоновщика «Неопределенные символы для архитектуры».
Lars Schneider
Измените его на [[[self class] alloc] init]; Больше тебе ничего не нужно.
Ник Тернер,
Вариант использования OP более чем законен - ​​это основа для любой сериализации иерархии объектов в файл / блок памяти / plist / что угодно. Часто вы НЕ ЗНАЕТЕ заранее, какие классы вам нужно создать. Мой вариант использования - утомительная необходимость "зарегистрировать" gazillion "NSValueTransformer" вместо дублирования [NSValueTransformer setValueTransformer: MyTransformerA alloc] init] forName: @ "MyTransformerA"]; 40 раз - я просто просматриваю NSArray имен трансформаторов - и создаю / регистрирую их из строки.
Мотти Шнеор
8

Если вы работаете с Objective-C без системы NeXTstep( OS X, iOSи GNUstepт. Д.) Или просто думаете, что этот метод чище, то вы можете использовать API библиотеки времени выполнения языка Objective-C . Под 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 ];

В Objective-C (1.0 или безымянная версия) вы должны использовать следующее:

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

Я не тестировал 1.0версию, но я использовал 2.0функцию в коде, который сейчас находится в производстве. Я лично считаю, что использование этой 2.0функции чище, если она доступна, чем функция NS, поскольку она занимает меньше места: the length of the name in bytes + 1 ( null terminator )для API 2.0 по сравнению с the sum of two pointers (isa, cstring)a size_t length (cstring_length)и length of the string in bytes + 1для NeXTSTEPAPI.

отметка
источник
2
@interface Magic : NSObject
+ (id)createInstanceOfClass:(Class)classe;
@end

@implementation Magic

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

@end

Затем использовать:

Car *car = [Magic createInstanceOfClass:[Car class]];
[car engineTurnOn];
Скела
источник