Jak pozbyć się ostrzeżenia „niezadeklarowany selektor”

162

Chcę użyć selektora na instancji NSObject bez potrzeby zaimplementowanego protokołu. Na przykład istnieje metoda kategorii, która powinna ustawić właściwość błędu, jeśli instancja NSObject, do której jest wywoływana, obsługuje ją. To jest kod, który działa zgodnie z przeznaczeniem:

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

Jednak kompilator nie widzi żadnej metody z setError: signature, więc wyświetla ostrzeżenie dla każdego wiersza zawierającego @selector(setError:)fragment:

Undeclared selector 'setError:'

Nie chcę deklarować protokołu, aby pozbyć się tego ostrzeżenia, ponieważ nie chcę, aby wszystkie klasy, które mogą go używać, implementowały cokolwiek specjalnego. Chciałbym, żeby miały jakąś setError:metodę lub właściwość.

Czy jest to wykonalne? W jaki sposób?

Pozdrawiam,
EP

epologee
źródło
2
Rozwiązanie jest dobrze wyjaśnione w performSelector może spowodować wyciek, ponieważ jego selektor jest nieznany
loretoparisi
Nieaktualny selektor spowoduje ostrzeżenie. Dostęp do selektora nie jest już bezpieczny, ponieważ selektor może zostać kiedyś usunięty.
DawnSong

Odpowiedzi:

254

Inną opcją byłoby wyłączenie ostrzeżenia za pomocą:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

Możesz umieścić ten wiersz w pliku .m, w którym pojawia się ostrzeżenie.

Aktualizacja:

Działa również z LLVM w następujący sposób:

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

... your code here ...

#pragma clang diagnostic pop
Klaas
źródło
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
dizy
tak, działa jak stwierdza @dizy. (Przepraszam za późną odpowiedź, ale przegapiłem powiadomienie).
Klaas
Ja alson potrzebowałem#pragma clang diagnostic ignored "-Wselector"
max
1
@mdorseif W większości przypadków ostrzeżenie, które „musisz” wykluczyć, jest wymienione w dzienniku kompilacji. Dzięki tej koncepcji możesz wyciszyć każde ostrzeżenie. Cieszę się, że dodałeś swoje dotyczące selektorów.
Klaas
@epologee, możesz zrobić to samo, ustawiając kompilację „Undeclared Selector”
194

Spójrz na NSSelectorFromString .

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

Pozwoli to na utworzenie selektora w czasie wykonywania zamiast kompilacji za pomocą @selectorsłowa kluczowego, a kompilator nie będzie miał szans na narzekanie.

sergio
źródło
Cześć @sergio, odpowiedzi zarówno Twoje, jak i @ jacobrelkin działają. Prawie wiele zgłoszonych jednocześnie. Czy pomożesz mi wybrać „lepszą” odpowiedź, jeśli taka istnieje?
epologee
2
Bardziej podoba mi się ta odpowiedź, ponieważ wygląda bardziej na „kakao” -y (?). Rzecz sel_registerName()wygląda na niejasną i taką, do której nie powinieneś dzwonić bezpośrednio, chyba że wiesz, co robisz, coś w rodzaju obj_msg_send ();)
Nicolas Miari
15
Nie jestem pewien, czy to Xcode 5, ale w przypadku tej implementacji otrzymuję inne ostrzeżenie: „PerformSelector może spowodować wyciek, ponieważ jego selektor jest nieznany” .
Hampden123
1
@ Hampden123: to inna kwestia. zajrzyj tutaj: stackoverflow.com/questions/7017281/…
sergio
52

Myślę, że dzieje się tak, ponieważ z jakiegoś dziwnego powodu selektor nie jest zarejestrowany w środowisku wykonawczym.

Spróbuj zarejestrować selektor przez sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}
Jacob Relkin
źródło
Cześć @jacobrelkin, zarówno twoje odpowiedzi, jak i @ sergio działają. Prawie wiele zgłoszonych jednocześnie. Czy pomożesz mi wybrać „lepszą” odpowiedź, jeśli taka istnieje?
epologee
2
@epologee i tak NSSelectorFromStringdzwoni sel_registerName()pod maską. Wybierz ten, który bardziej Ci odpowiada.
Jacob Relkin
1
@epologee Myślę, że sel_registerName()bezpośrednie dzwonienie jest bardziej wyraźne, jeśli chodzi o to, dlaczego to robisz. NSSelectorFromStringnie mówi , że będzie próbował zarejestrować selektor.
Jacob Relkin
8
Nie jestem pewien, czy to Xcode 5, ale w przypadku tej implementacji otrzymuję inne ostrzeżenie: „PerformSelector może spowodować wyciek, ponieważ jego selektor jest nieznany” .
Hampden123
@ Max_Power89 Nie. Zobacz moje inne komentarze poniżej. Nie chciałem spędzać nad tym zbyt wiele czasu, więc po prostu dołączyłem pliki nagłówkowe.
Hampden123
7

Otrzymałem ten komunikat, aby zniknął, # dołączając plik do metody. Nic innego nie zostało użyte z tego pliku.

Mark Patterson
źródło
Chociaż jest to mniej wdzięczne rozwiązanie, działa w moim przypadku, ponieważ mam „znanych podejrzanych”, którzy mogą otrzymywać selektor. Ponadto, jeśli zaimplementuję podejście z selektorem środowiska wykonawczego, nadal otrzymam inne ostrzeżenie w instrukcji performSelector; mianowicie, „PerformSelector może spowodować wyciek, ponieważ jego selektor jest nieznany” . Więc dziękuję!
Hampden123
2
Żadna z najwyżej ocenionych odpowiedzi nie jest poprawna. Celem ostrzeżenia „niezadeklarowany selektor” jest wychwycenie błędów w czasie kompilacji, jeśli zmienisz nazwę selektora, na którym polegałeś. Dlatego najlepiej # zaimportować plik, który deklaruje metodę, na której polegałeś.
Brane
7

Zdaję sobie sprawę, że trochę się spóźniłem z tym wątkiem, ale dla kompletności możesz globalnie wyłączyć to ostrzeżenie, używając docelowych ustawień kompilacji.

W sekcji „Ostrzeżenia Apple LLVM - Cel-C” zmień:

Undeclared Selector - NO
Kichot
źródło
6

Jeśli twoja klasa implementuje metodę setError: (nawet poprzez zadeklarowanie dynamicznej metody ustawiającej ewentualną właściwość błędu), możesz chcieć zadeklarować ją w swoim pliku interfejsu (.h) lub jeśli nie lubisz pokazywać tego w ten sposób, możesz spróbuj z podstępną sztuczką PrivateMethods:

@interface Yourclass (PrivateMethods)

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

@end

tuż przed Twoją @implementacją powinno to ukryć ostrzeżenia;).

i_mush
źródło
Dzięki, ale wywołuję metodę z kategorii, więc to nie ma zastosowania. Pozdrawiam, EP.
epologee
A niektórzy z nas robią rzeczy bardziej egzotyczne - w moim przypadku selektor jest zaimplementowany w obiekcie F #.
James Moore,
1
To nie eliminuje ostrzeżenia w XCode 7.1.1 / iOS 9.1, widzęPerformSelector may cause a leak because its selector is unknown
loretoparisi
3

Naprawdę wygodne makro umieścić w swoim .pchlub Common.hczy gdziekolwiek chcesz:

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

To edycja tego pytania w podobnym wydaniu ...

Aviel Gross
źródło
3

Możesz to wyłączyć w Xcode jak na zrzucie ekranu:

wprowadź opis obrazu tutaj

Witaj świecie
źródło
Niezłe. Mimo to wolę wyłączać ostrzeżenie tylko w wyraźnych przypadkach, mówiąc „brzęk jest zły przy tej okazji, wiem, co robię”. Dzięki za wkład!
epologee
2

Możesz również najpierw rzutować obiekt na identyfikator, aby uniknąć ostrzeżenia:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}
Oszust
źródło
1
Nie eliminuje to tego samego ostrzeżenia dotyczącego zawartości wyrażenia if, aż do XC7.1 do dnia dzisiejszego.
Martin-Gilles Lavoie
2

Innym sposobem uniknięcia tego ostrzeżenia jest upewnienie się, że metoda selektora wygląda następująco:

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

Nie zapomnij o „(id) nadawca”, jeśli chcesz zaakceptować dowolnego nadawcę lub, jeśli wolisz, określić typ obiektu nadawcy.

Odpoczynek
źródło
0

Chociaż prawidłowa odpowiedź prawdopodobnie polega na poinformowaniu Xcode poprzez import lub zarejestrowanie selektora, że ​​taki selektor istnieje, w moim przypadku brakowało średnika. Zanim „naprawisz” błąd, upewnij się, że być może błąd jest poprawny, a Twój kod nie. Znalazłem błąd na przykład w przykładzie Apple MVCNetworking.

Louis St-Amour
źródło
Nie, poprawna odpowiedź nie polegała na informowaniu Xcode poprzez import, ponieważ ten import był na miejscu. Prawidłowa odpowiedź była odpowiedzią powyżej, która została oznaczona jako ... poprawna odpowiedź, chociaż odpowiedź @ sergio również rozwiązałaby problem. Użycie niewłaściwego selektora nie jest przedmiotem tego pytania, dlatego zmiana selektora nie jest odpowiedzią. Oszczędzę ci jednak głosów przeciw.
epologee
1
Dzięki za przypomnienie mi, że prawdopodobnie powinienem był użyć komentarza. Wszystko, co mogę powiedzieć, to to, że brakujące importy powodują również to ostrzeżenie Xcode, jeśli nie to konkretne wystąpienie. Poleciłbym NSSelectorFromString lub inne takie opcje „rejestracji” tylko podczas budowania selektora w czasie wykonywania lub odpowiadania na wywołania metod w sposób dynamiczny (np. MethodSignatureForSelector). Zarejestrowanie tego oznacza, że ​​"pracujesz nad błędem", a więc nie jest poprawne w niektórych okolicznościach, ponieważ bardziej poprawnym podejściem byłoby naprawienie ostrzeżenia (to znaczy, jeśli analiza dzwonienia była prawidłowa).
Louis St-Amour
Faktycznie, teraz widzę, że pierwotne pytanie jasno mówi: „bez potrzeby zaimplementowanego protokołu” - iw ogóle nie wspomina o imporcie. Stwierdziłbym więc, że import samej kategorii może być najlepszą opcją dla tego użytkownika. Cokolwiek innego tutaj mogłoby dwukrotnie zdefiniować selektor, mówiąc technicznie. Tak? - Edycja: Ach, posunąłem się za daleko. Dziękuję za odpowiedź, teraz przestanę. :)
Louis St-Amour
-1

Udało mi się uzyskać ostrzeżenie, aby odejść, dodając żadną metodę (ujawnienie: nie pomyślałem o tym, ale znalazłem to, wpisując w Google w zaplanowanym czasie z interwałem czasowym)

    [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
{

}

Chociaż doceniam to, że wiem, jak ukryć ostrzeżenie, naprawienie go jest lepsze, a techniki Sergio ani Relkin nie zadziałały dla mnie z nieznanych powodów.

user938797
źródło
1
Jeśli ktoś inny przeczyta to rozwiązanie, które zadziała , będzie dość zdezorientowany, w tym twoje przyszłe ja. Jeśli jesteś pewien, że wiesz, co robisz, wywołując nieistniejący selektor, powodując w ten sposób ostrzeżenie, pomiń wprowadzający w błąd skrót metody i upewnij się, że kod wyraża Twoją intencję.
epologie
1
Słuszna uwaga. Pracowałem z kodem odziedziczonym i po prostu próbowałem wymyślić, jak usunąć ostrzeżenie, nie próbując rozwiązać podstawowego pytania, dlaczego nie istnieje selektor. Zawsze powtarzam krok po kroku.
user938797