Bagaimana cara menghilangkan peringatan 'pemilih tidak diumumkan'

162

Saya ingin menggunakan pemilih pada instance NSObject tanpa perlu protokol yang diimplementasikan. Misalnya, ada metode kategori yang harus menetapkan properti kesalahan jika instance NSObject yang dipanggil mendukungnya. Ini kodenya, dan kodenya berfungsi sebagaimana dimaksud:

if ([self respondsToSelector:@selector(setError:)])
{
    [self performSelector:@selector(setError:) withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}

Namun, kompiler tidak melihat metode apa pun dengan setError: signature, sehingga memberi saya peringatan, untuk setiap baris yang berisi @selector(setError:)potongan:

Undeclared selector 'setError:'

Saya tidak ingin harus mendeklarasikan protokol untuk menghilangkan peringatan ini, karena saya tidak ingin semua kelas yang menggunakan ini untuk mengimplementasikan sesuatu yang istimewa. Hanya dengan konvensi, saya ingin mereka memiliki setError:metode atau properti.

Apakah ini bisa dilakukan? Bagaimana?

Cheers,
EP

Epologee
sumber
2
Solusinya dijelaskan dengan baik di performSelector dapat menyebabkan kebocoran karena
pemilihnya
Pemilih yang sudah usang akan menyebabkan peringatan. Tidak lagi aman untuk mengakses pemilih karena pemilih mungkin dihapus pada suatu waktu.
DawnSong

Jawaban:

254

Opsi lain adalah menonaktifkan peringatan dengan:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

Anda dapat menempatkan baris ini di file .m di mana peringatan itu terjadi.

Memperbarui:

Ia bekerja juga dengan LLVM seperti ini:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"

... your code here ...

#pragma clang diagnostic pop
Klaas
sumber
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
pusing
ya, itu tidak seperti @dizy menyatakan. (Maaf atas jawaban yang terlambat, tetapi saya melewatkan pemberitahuan).
Klaas
Saya alson diperlukan#pragma clang diagnostic ignored "-Wselector"
maks
1
@ mdorseif Sebagian besar waktu peringatan yang harus Anda kecualikan tercantum dalam log kompilasi. Anda dapat membisukan peringatan apa pun dengan konsep ini. Senang Anda menambahkan Anda tentang penyeleksi.
Klaas
@epologee Anda dapat melakukan hal yang sama melalui pengaturan build "Undeclared Selector"
194

Lihatlah NSSelectorFromString .

 SEL selector = NSSelectorFromString(@"setError:");
 if ([self respondsToSelector:selector])

Ini akan memungkinkan Anda untuk membuat pemilih pada saat runtime, bukan pada waktu kompilasi melalui @selectorkata kunci, dan kompiler tidak akan memiliki kesempatan untuk mengeluh.

sergio
sumber
Hai @sergio, jawaban Anda dan @ jacobrelkin berfungsi. Cukup banyak yang dikirimkan secara bersamaan. Maukah Anda membantu saya memilih jawaban 'lebih baik', jika ada?
epologee
2
Saya lebih suka jawaban ini karena lebih terlihat "Kakao" -y (?). Hal yang sel_registerName()terlihat tidak jelas dan jenis yang Anda tidak boleh menelepon langsung kecuali Anda tahu apa yang Anda lakukan, agak seperti obj_msg_send ();)
Nicolas Miari
15
Tidak yakin apakah itu Xcode 5, tapi saya mendapat peringatan berbeda dengan implementasi ini: "PerformSelector dapat menyebabkan kebocoran karena pemilihnya tidak diketahui" .
Hampden123
1
@ Hampden123: itu masalah yang berbeda. lihat di sini: stackoverflow.com/questions/7017281/…
sergio
52

Saya pikir ini karena beberapa alasan aneh pemilih tidak terdaftar dengan runtime.

Coba daftarkan pemilih melalui sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}
Jacob Relkin
sumber
Hai @ jacobrelkin, jawaban Anda dan @ sergio berfungsi. Cukup banyak yang dikirimkan secara bersamaan. Maukah Anda membantu saya memilih jawaban 'lebih baik', jika ada?
epologee
2
Lagipula @epologee NSSelectorFromStringmemanggil di sel_registerName()bawah tenda. Pilih mana yang lebih cocok untuk Anda.
Jacob Relkin
1
@epologee Saya pikir menelepon sel_registerName()langsung lebih eksplisit tentang mengapa Anda melakukannya. NSSelectorFromStringtidak memberi tahu Anda bahwa itu akan mencoba mendaftarkan pemilih.
Jacob Relkin
8
Tidak yakin apakah itu Xcode 5, tapi saya mendapat peringatan berbeda dengan implementasi ini: "PerformSelector dapat menyebabkan kebocoran karena pemilihnya tidak diketahui" .
Hampden123
@ Max_Power89 No. Lihat komentar saya yang lain di bawah. Saya tidak ingin menghabiskan terlalu banyak waktu untuk ini, jadi saya cukup memasukkan file header.
Hampden123
7

Saya mendapat pesan itu untuk pergi dengan # memasukkan file dengan metode. Tidak ada lagi yang digunakan dari file itu.

Mark Patterson
sumber
Meskipun ini adalah solusi yang kurang anggun, itu bekerja untuk saya karena saya memiliki "tersangka yang dikenal" yang mungkin menerima pemilih. Juga, jika saya menerapkan pendekatan pemilih runtime, saya masih akan mendapatkan peringatan berbeda di pernyataan performSelector; yaitu, "PerformSelector dapat menyebabkan kebocoran karena pemilihnya tidak diketahui" . Jadi terima kasih!
Hampden123
2
Tidak satu pun dari jawaban terpilih teratas adalah benar. Maksud dari peringatan "pemilih yang tidak dideklarasikan" adalah untuk menangkap kesalahan pada waktu kompilasi jika Anda mengubah nama pemilih yang Anda andalkan. Jadi paling benar untuk mengimpor file yang menyatakan metode yang Anda andalkan.
Brane
7

Saya sadar saya agak terlambat untuk utas ini tetapi untuk kelengkapan, Anda dapat mematikan peringatan ini secara global menggunakan pengaturan target build.

Pada bagian, 'peringatan Apple LLVM - Objective-C', ubah:

Undeclared Selector - NO
Quixiote
sumber
6

Jika kelas Anda mengimplementasikan metode setError: (bahkan dengan mendeklarasikan dinamis setter dari properti error akhirnya) Anda mungkin ingin mendeklarasikannya dalam file antarmuka Anda (.h), atau jika Anda tidak ingin menunjukkannya dengan cara yang Anda bisa coba dengan trik rumit PrivateMethods:

@interface Yourclass (PrivateMethods)

- (void) yourMethod1;
- (void) yourMethod2;

@end

tepat sebelum @ implementasi Anda, ini seharusnya menyembunyikan peringatan;).

i_mush
sumber
Terima kasih, tapi saya memanggil metode dari kategori, jadi ini tidak berlaku. Cheers, EP.
Epologee
Dan beberapa dari kita melakukan hal-hal yang lebih eksotis - pemilih diimplementasikan dalam objek F #, dalam kasus saya.
James Moore
1
Ini tidak menghilangkan peringatan di XCode 7.1.1 / iOS 9.1, saya bisa melihatPerformSelector may cause a leak because its selector is unknown
loretoparisi
3

Sebuah makro benar-benar nyaman untuk dimasukkan ke dalam Anda .pchatau Common.hatau di mana pun Anda inginkan:

#define SUPPRESS_UNDECLARED_SELECTOR_LEAK_WARNING(code)                        \
_Pragma("clang diagnostic push")                                        \
_Pragma("clang diagnostic ignored \"-Wundeclared-selector"\"")     \
code;                                                                   \
_Pragma("clang diagnostic pop")                                         \

Ini pengeditan dari pertanyaan ini untuk masalah serupa ...

Aviel Gross
sumber
3

Anda dapat mematikannya di Xcode seperti di tangkapan layar:

masukkan deskripsi gambar di sini

Halo Dunia
sumber
Bagus Namun, saya lebih memilih menonaktifkan peringatan hanya untuk kasus-kasus eksplisit, dengan cara mengatakan "dentang salah dalam kesempatan ini, saya tahu apa yang saya lakukan". Terima kasih atas masukan Anda!
Epologee
2

Anda juga dapat melemparkan objek tersebut ke id terlebih dahulu untuk menghindari peringatan:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}
Penipu
sumber
1
Ini tidak menghilangkan peringatan yang sama pada konten ekspresi if, sampai XC7.1 hingga hari ini.
Martin-Gilles Lavoie
2

Cara lain untuk menghindari peringatan ini adalah memastikan metode pemilih Anda terlihat seperti ini:

-(void) myMethod :(id) sender{
}

Jangan lupa "pengirim" (id) "jika Anda ingin menerima pengirim apa pun atau tentukan jenis objek pengirim jika Anda mau.

Istirahat
sumber
0

Sementara jawaban yang benar kemungkinan terletak pada menginformasikan Xcode melalui impor atau mendaftarkan pemilih bahwa pemilih seperti itu ada, dalam kasus saya, saya kehilangan tanda titik koma. Pastikan sebelum Anda "memperbaiki" kesalahan yang mungkin, kesalahan itu benar dan kode Anda tidak. Saya menemukan kesalahan dalam sampel MVCNetworking Apple, misalnya.

Louis St-Amour
sumber
Tidak, jawaban yang benar adalah tidak menginformasikan Xcode melalui impor, karena impor itu sudah ada. Jawaban yang benar adalah jawaban di atas yang ditandai sebagai ... jawaban yang benar, meskipun jawaban @ sergio juga akan menyelesaikan masalah. Menggunakan pemilih yang salah bukanlah subjek dari pertanyaan ini, oleh karena itu mengubah pemilih bukan jawaban. Saya akan menyelamatkan Anda downvote.
Epologee
1
Terima kasih telah mengingatkan saya bahwa saya mungkin harus menggunakan komentar. Yang bisa saya katakan adalah bahwa impor yang hilang juga menyebabkan peringatan Xcode ini, jika bukan contoh khusus ini. Saya hanya akan merekomendasikan NSSelectorFromString atau opsi "pendaftaran" lainnya ketika membangun pemilih saat runtime atau menanggapi panggilan metode secara dinamis (mis. MethodSignatureForSelector). Mendaftarkannya berarti Anda "mengatasi kesalahan" dan karena itu tidak benar untuk beberapa keadaan, karena pendekatan yang lebih tepat adalah memperbaiki peringatan (jika analisis dentang itu benar, yaitu.)
Louis St-Amour
Bahkan, saya sekarang melihat bahwa pertanyaan aslinya dengan jelas mengatakan, "tanpa perlu protokol yang diimplementasikan" - dan tidak menyebutkan impor sama sekali. Jadi saya akan mengajukan bahwa mengimpor kategori itu sendiri mungkin merupakan opsi terbaik untuk pengguna ini. Hal lain di sini dapat mendefinisikan pemilih dua kali, secara teknis. Iya? - Sunting: Ah, saya sudah sejauh ini. Terima kasih atas tanggapan Anda, saya akan berhenti sekarang. :)
Louis St-Amour
-1

Saya bisa mendapatkan peringatan untuk pergi dengan menambahkan metode yang menenangkan (pengungkapan: Saya tidak memikirkan hal ini tetapi menemukannya dengan googling pada jadwal yang lebih lama dengan waktu tambahan)

    [NSTimer scheduledTimerWithTimeInterval:[[NSDate distantFuture] timeIntervalSinceNow]
                                     target:self
                                   selector:@selector(donothingatall:)
                                   userInfo:nil
                                    repeats:YES];


    [[NSRunLoop currentRunLoop] run];

    HTTPLogVerbose(@"%@: BonjourThread: Aborted", THIS_FILE);

    }
}

+ (void) donothingatall:(NSTimer *)timer
{

}

Sementara saya menghargai mengetahui cara menyembunyikan peringatan, memperbaikinya lebih baik dan teknik Sergio maupun Relkin tidak bekerja untuk saya, karena alasan yang tidak diketahui.

pengguna938797
sumber
1
Jika orang lain membaca solusi ini, yang akan berhasil , dia akan sangat bingung, termasuk diri Anda di masa depan. Jika Anda yakin tahu apa yang Anda lakukan dengan memanggil pemilih yang tidak ada, sehingga menyebabkan peringatan, lewati rintisan metode menyesatkan dan pastikan kode Anda menyatakan niat Anda.
epologee
1
Poin yang bagus. Saya sedang bekerja dengan kode yang diwarisi dan hanya mencoba mencari cara untuk membuat peringatan itu pergi, tidak mencoba untuk memecahkan pertanyaan dasar mengapa ada pemilih yang tidak ada. Selangkah demi selangkah, saya selalu mengatakan.
user938797