Buat instance kelas objektif-c dengan nama?

95

Apakah mungkin untuk membuat instance kelas dengan nama? Sesuatu seperti:

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

Saya tidak tahu apakah ini mungkin dalam obyektif-c tetapi sepertinya akan,

Menandai
sumber

Jawaban:

217
id object = [[NSClassFromString(@"NameofClass") alloc] init];
Chris McCall
sumber
Ini tidak akan menyebabkan kebocoran?
AntiMoron
38

NSClassFromString()menjalankan risiko salah ketik nama kelas atau sebaliknya menggunakan kelas yang tidak ada. Anda tidak akan mengetahuinya sampai runtime jika Anda membuat kesalahan itu. Sebaliknya, jika Anda menggunakan tipe objektif-c bawaan Classuntuk membuat variabel, maka compiler akan memverifikasi bahwa kelas tersebut ada.

Misalnya, di .h:

@property Class NameOfClass;

dan kemudian di .m:

id object = [[NameOfClass alloc] init];

Jika Anda salah mengetik nama kelas atau jika tidak ada, Anda akan mendapatkan kesalahan pada waktu kompilasi. Juga saya pikir ini adalah kode yang lebih bersih.

Simon Woodside
sumber
ini dia, sobat. Tidak sepenuhnya yakin ini adalah jawaban terbaik, karena memerlukan dua baris dan kurang dinamis, tetapi mendapat suara positif semuanya
Chris McCall
Saya kira Anda bisa mengatakan bahwa itu kurang dinamis karena saya menggunakan simbol, bukan string. Namun, jika Anda mengetahui kelas yang Anda inginkan saat menulis kode, lebih baik menggunakan simbol untuk menghindari kemungkinan kesalahan ketik.
Simon Woodside
@sbwoodside: Bagaimana ini bisa bekerja? Saya mencobanya dan saya mendapatkan "Simbol yang tidak ditentukan untuk arsitektur" dari linker.
Lars Schneider
Ubah ke [[[self class] alokasi] init]; Anda tidak membutuhkan yang lain.
Nick Turner
Kasus penggunaan OP lebih dari sekadar sah - ini adalah dasar untuk serialisasi hierarki objek ke file / memory-block / plist / apa saja. Sering kali Anda TIDAK TAHU sebelumnya kelas mana yang perlu Anda buat. Kasus penggunaan saya adalah kebutuhan yang membosankan untuk "mendaftarkan" gazillion "NSValueTransformer" dan bukannya menduplikasi [NSValueTransformer setValueTransformer: MyTransformerA alokasi] init] forName: @ "MyTransformerA"]; 40 kali - Saya baru saja memindai NSArray nama transformator - dan membuat / mendaftarkannya dari string.
Motti Shneor
8

Jika Anda bekerja dengan Objective-C tanpa NeXTstep( OS X, iOS, GNUstepdll) sistem atau Anda hanya berpikir metode ini lebih bersih, maka Anda bisa memanfaatkan bahasa Objective-C runtime perpustakaan API . Di bawah 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 ];

Di bawah Objective-C (1.0 atau versi tanpa nama) Anda akan menggunakan yang berikut:

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

Saya belum menguji 1.0versinya, namun saya telah menggunakan 2.0fungsi dalam kode yang sekarang dalam produksi. Saya pribadi percaya menggunakan 2.0fungsi lebih bersih jika tersedia daripada fungsi NS karena mengkonsumsi lebih sedikit ruang: the length of the name in bytes + 1 ( null terminator )untuk 2.0 API versus the sum of two pointers (isa, cstring), a size_t length (cstring_length), dan length of the string in bytes + 1untuk NeXTSTEPAPI.

Menandai
sumber
2
@interface Magic : NSObject
+ (id)createInstanceOfClass:(Class)classe;
@end

@implementation Magic

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

@end

Kemudian untuk menggunakannya:

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