Objective-C: Memanggil penyeleksi dengan banyak argumen

142

Di MyClass.m, saya telah mendefinisikan

- (void) myTest: (NSString *) withAString{
    NSLog(@"hi, %@", withAString);
}

dan deklarasi yang sesuai di MyClass.h. Kemudian saya ingin menelepon

[self performSelector:@selector(mytest:withAString:) withObject: mystring];

di MyClass.m tapi saya mendapatkan kesalahan yang mirip dengan * Mengakhiri aplikasi karena pengecualian 'NSInvalidArgumentException' yang tidak tertangkap, alasan: '* - [MyClass myTest: withAtring:]: pemilih yang tidak dikenal dikirim ke instance 0xe421f0'

Saya mencoba kasus sederhana dengan pemilih yang tidak mengambil argumen yang mencetak string ke konsol dan itu berfungsi dengan baik. Apa yang salah dengan kode dan bagaimana cara memperbaikinya? Terima kasih.

Stu
sumber
4
Posting Anda menanyakan tentang 'berbagai argumen', tetapi Anda hanya menggunakan satu. Sekarang saya ingin tahu tentang bagaimana seseorang AKAN melakukannya dengan beberapa argumen, selain membungkusnya dalam array / dict / apa pun.
RonLugge

Jawaban:

137

Tanda tangan metode Anda adalah:

- (void) myTest:(NSString *)

withAString kebetulan menjadi parameter (namanya menyesatkan, sepertinya itu adalah bagian dari tanda tangan pemilih).

Jika Anda memanggil fungsi dengan cara ini:

[self performSelector:@selector(myTest:) withObject:myString];

Itu akan berhasil.

Tapi, seperti yang disarankan poster lain, Anda mungkin ingin mengganti nama metode:

- (void)myTestWithAString:(NSString*)aString;

Dan telepon:

[self performSelector:@selector(myTestWithAString:) withObject:myString];
Lyndsey Ferguson
sumber
2
Sekarang saya melihat orang mendapat manfaat dari jawaban ini, saya telah meninjau tanggapan saya; Saya akan menyarankan agar panggilan itu hanya: - (void) testWithString: (NSString *) aString;
Lyndsey Ferguson
313

Di Objective-C, tanda tangan pemilih terdiri dari:

  1. Nama metode (dalam hal ini akan menjadi 'myTest') (wajib)
  2. A ':' (titik dua) mengikuti nama metode jika metode memiliki input.
  3. Nama dan ':' untuk setiap input tambahan.

Selektor tidak memiliki pengetahuan tentang:

  1. Jenis input
  2. Jenis pengembalian metode.

Berikut ini adalah implementasi kelas di mana metode performMethodsViaSelectors melakukan metode kelas lainnya melalui penyeleksi:

@implementation ClassForSelectors
- (void) fooNoInputs {
    NSLog(@"Does nothing");
}
- (void) fooOneIput:(NSString*) first {
    NSLog(@"Logs %@", first);
}
- (void) fooFirstInput:(NSString*) first secondInput:(NSString*) second {
    NSLog(@"Logs %@ then %@", first, second);
}
- (void) performMethodsViaSelectors {
    [self performSelector:@selector(fooNoInputs)];
    [self performSelector:@selector(fooOneInput:) withObject:@"first"];
    [self performSelector:@selector(fooFirstInput:secondInput:) withObject:@"first" withObject:@"second"];
}
@end

Metode yang Anda inginkan untuk membuat pemilih memiliki input tunggal, jadi Anda akan membuat pemilih untuk itu seperti:

SEL myTestSelector = @selector(myTest:);
Shane Arney
sumber
3
Jawaban yang bagus. Untuk sedikit memperjelas, Anda nama pemilih HARUS memiliki setidaknya satu bagian, yang mungkin atau mungkin tidak mengambil parameter - jika ya, itu harus memiliki titik dua. Nama pemilih dengan dua atau lebih bagian HARUS memiliki tanda titik dua setelah SETIAP bagian - adalah tidak sah untuk memiliki pemilih dalam bentuk "-useFoo: andBar: toDoSomething".
Quinn Taylor
Terima kasih untuk ini. Saya sudah berjuang dengan ini untuk sementara waktu, senang atas bantuannya!
James Hall
bagaimana dengan parameter input angka integer? apa yang harus dilakukan dalam kasus ini?
Hoang Pham
1
Anda harus membungkus integer dalam objek NSNumber (lihat developer.apple.com/library/ios/#documentation/Cocoa/Reference/… ), dan mengambil nilai integer di tubuh metode yang disebut. Itu bisa sedikit verbose (dan saya belum menemukan cara yang lebih baik di sekitarnya) tetapi berfungsi dengan baik.
Shane Arney
30
+100: Ini luar biasa! Saya tidak tahu tentang cara menggunakan beberapa parameter "withObject:". Saya akan memperbaiki ini seratus kali jika saya bisa ...
FreeAsInBeer
13

@Shane Arney

performSelector:withObject:withObject:

Anda mungkin juga ingin menyebutkan bahwa metode ini hanya untuk melewati maksimum 2 argumen, dan tidak dapat ditunda. (sepertiperformSelector:withObject:afterDelay:) .

agak aneh bahwa apel hanya mendukung 2 objek untuk dikirim dan tidak membuatnya lebih umum.

Lirik
sumber
2
Terimakasih atas infonya. Saya tidak bisa mendapatkan penundaan untuk bekerja dan sekarang saya tahu mengapa. FYI, untuk menyiasati batas dua objek, saya melewati sebuah array dan kemudian menggunakannya dalam metode.
JScarry
7

Kode Anda memiliki dua masalah. Satu diidentifikasi dan dijawab, tetapi yang lainnya tidak. Yang pertama adalah pemilih Anda tidak memiliki nama parameternya. Namun, bahkan ketika Anda memperbaikinya, garis masih akan memunculkan pengecualian, dengan asumsi tanda tangan metode revisi Anda masih mencakup lebih dari satu argumen. Katakanlah metode revisi Anda dinyatakan sebagai:

-(void)myTestWithString:(NSString *)sourceString comparedTo:(NSString *)testString ;

Membuat penyeleksi untuk metode yang mengambil banyak argumen benar-benar valid (misalnya @selektor (myTestWithString: compareTo :)). Namun, metode performSelector hanya memungkinkan Anda untuk melewatkan satu nilai ke myTest, yang sayangnya memiliki lebih dari satu parameter. Ini akan kesalahan dan memberi tahu Anda bahwa Anda tidak memberikan nilai yang cukup.

Anda selalu dapat mendefinisikan kembali metode Anda untuk mengambil koleksi karena itu hanya parameter:

-(void)myTestWithObjects:(NSDictionary *)testObjects ;

Namun, ada solusi yang lebih elegan (yang tidak memerlukan refactoring). Jawabannya adalah menggunakan NSInvocation, beserta metodenya setArgument:atIndex:dan invoke.

Saya sudah menulis artikel, termasuk contoh kode , jika Anda ingin lebih detail. Fokusnya adalah pada threading, tetapi dasar-dasarnya masih berlaku.

Semoga berhasil!

Zack
sumber
3

Tanda tangan metode Anda tidak masuk akal, apakah Anda yakin itu bukan kesalahan ketik? Saya tidak jelas bagaimana cara kompilasi, meskipun mungkin Anda mendapatkan peringatan yang Anda abaikan?

Berapa banyak parameter yang Anda harapkan dari metode ini?

Rob Napier
sumber
Maaf kamu sudah menulis. Saya mengetik dan mencoba membuatnya lebih sederhana daripada menyalin dan menempelkan kode saya, tetapi saya membuat kesalahan dalam prosesnya. Saya mengharapkan metode ini untuk mengambil satu parameter; string yang ingin saya cetak.
Stu
2

Pikirkan kelas harus didefinisikan sebagai:

- (void) myTestWithSomeString:(NSString *) astring{
    NSLog(@"hi, %s", astring);
}

Anda hanya memiliki satu parameter sehingga Anda hanya memiliki satu:

Anda mungkin ingin mempertimbangkan untuk menggunakan% @ di NSLog Anda juga - ini adalah kebiasaan yang baik untuk masuk - kemudian akan menuliskan objek apa pun - bukan hanya string.

Grouchal
sumber
-1

Pengguna iOS juga mengharapkan otokapitalisasi: Dalam bidang teks standar, huruf pertama dari sebuah kalimat dalam bahasa yang case-sensitive secara otomatis dikapitalisasi.

Anda dapat memutuskan apakah akan menerapkan fitur tersebut atau tidak; tidak ada API khusus untuk semua fitur yang baru saja terdaftar, jadi menyediakannya adalah keunggulan kompetitif.

Dokumen Apple mengatakan tidak ada API yang tersedia untuk fitur ini dan beberapa fitur lain yang diharapkan dalam papan tombol kustom. jadi Anda perlu mencari tahu logika Anda sendiri untuk mengimplementasikan ini.

Kannan Prasad
sumber