Bagaimana cara membuat pemilih secara dinamis saat runtime dengan Objective-C?

93

Saya tahu cara membuat SELwaktu kompilasi menggunakan @selector(MyMethodName:)tetapi yang ingin saya lakukan adalah membuat selektor secara dinamis dari file NSString. Apakah ini mungkin?

Apa yang saya bisa lakukan:

SEL selector = @selector(doWork:);
[myobj respondsToSelector:selector];

Yang ingin saya lakukan: (pseudo code, ini jelas tidak berfungsi)

SEL selector = selectorFromString(@"doWork");
[myobj respondsToSelector:selector];

Saya telah mencari dokumen Apple API, tetapi belum menemukan cara yang tidak bergantung pada @selector(myTarget:)sintaks waktu kompilasi .

craigb.dll
sumber

Jawaban:

180

Saya bukan programmer Objective-C, hanya simpatisan, tapi mungkin NSSelectorFromString adalah yang Anda butuhkan. Disebutkan secara eksplisit dalam Referensi Runtime yang dapat Anda gunakan untuk mengonversi string menjadi selektor.

Torsten Marek
sumber
5
Saya perlu memoles Google-fu saya. itulah yang saya (atau tidak mungkin) cari.
craigb
Yah, saya masih memiliki tautan yang beterbangan di bookmark saya sejak saya membaca dokumen Objective-C 2.0 beberapa hari yang lalu.
Torsten Marek
40

Menurut dokumentasi XCode, psuedocode Anda pada dasarnya sudah benar.

Paling efisien untuk menetapkan nilai ke variabel SEL pada waktu kompilasi dengan direktif @selector (). Namun, dalam beberapa kasus, program mungkin perlu mengubah string karakter menjadi pemilih saat runtime. Ini dapat dilakukan dengan fungsi NSSelectorFromString:

setWidthHeight = NSSelectorFromString(aBuffer);

Edit: Nyebelin, terlalu lambat. : P

Josh Gagnon
sumber
2
NSStringFromSelector(@"doWork")mengubahnya dengan cara lain (hanya fyi)
bendytree
8
Saya pikir maksud Anda, NSStringFromSelector (@selector (doWork))
jpswain
Dan apa yang seharusnya dilakukan oleh selektor itu? Bukankah kita harus menentukan blok atau sesuatu?
pengguna4951
12

Saya harus mengatakan bahwa ini sedikit lebih rumit daripada yang mungkin disarankan oleh jawaban responden sebelumnya ... jika Anda memang benar-benar ingin membuat pemilih ... bukan hanya "panggil satu" yang "Anda miliki" .. .

Anda perlu membuat penunjuk fungsi yang akan dipanggil oleh metode "baru" Anda .. jadi untuk metode seperti [self theMethod:(id)methodArg];, Anda akan menulis ...

void (^impBlock)(id,id) = ^(id _self, id methodArg) { 
     [_self doSomethingWith:methodArg]; 
};

lalu Anda perlu membuat IMP blok secara dinamis, kali ini, meneruskan, "self", the SEL, dan argumen apa pun ...

void(*impFunct)(id, SEL, id) = (void*) imp_implementationWithBlock(impBlock);

dan menambahkannya ke kelas Anda, bersama dengan tanda tangan metode yang akurat untuk seluruh pengisap (dalam kasus ini "v@:@" , void return, pemanggil objek, argumen objek)

 class_addMethod(self.class, @selector(theMethod:), (IMP)impFunct, "v@:@");

Anda dapat melihat beberapa contoh bagus dari jenis runtime shenanigans ini , di salah satu repo saya, di sini.

Alex Gray
sumber
5

Saya tahu ini telah dijawab sejak lama, tetapi saya tetap ingin berbagi. Ini bisa dilakukan dengan menggunakan sel_registerNamejuga.

Kode contoh dalam pertanyaan dapat ditulis ulang seperti ini:

SEL selector = sel_registerName("doWork:");
[myobj respondsToSelector:selector];
Krypton
sumber
2
Sebenarnya, yang NSSelectorFromStringdisebutkan oleh @ torsten-marek menggunakan di sel_registerNamebawah tenda. appledev : "NSSelectorFromString meneruskan representasi karakter berenkode UTF-8 dari aSelectorName ke sel_registerName dan mengembalikan nilai yang dikembalikan oleh fungsi itu"
PLG