Come eliminare l'avviso "Selettore non dichiarato"


162

Voglio usare un selettore su un'istanza di NSObject senza la necessità di un protocollo implementato. Ad esempio, esiste un metodo di categoria che dovrebbe impostare una proprietà di errore se l'istanza NSObject su cui viene chiamata la supporta. Questo è il codice e il codice funziona come previsto:

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

Tuttavia, il compilatore non vede alcun metodo con setError: signature, quindi mi dà un avvertimento, per ogni riga che contiene lo @selector(setError:)snippet:

Undeclared selector 'setError:'

Non voglio dichiarare un protocollo per sbarazzarmi di questo avvertimento, perché non voglio tutte le classi che potrebbero usarlo per implementare qualcosa di speciale. Solo per convenzione voglio che abbiano un setError:metodo o una proprietà.

È fattibile? Come?

Saluti,
EP



Un selettore obsoleto causerà l'avviso. Non è più possibile accedere al selettore perché il selettore potrebbe essere rimosso in qualche momento.
DawnSong,

Risposte:


254

Un'altra opzione sarebbe quella di disabilitare l'avviso con:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

È possibile posizionare questa riga nel file .m in cui si verifica l'avviso.

Aggiornare:

Funziona anche con LLVM in questo modo:

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

... your code here ...

#pragma clang diagnostic pop

#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
dizy

sì, fa come afferma @dizy. (Ci scusiamo per la risposta tardiva, ma ho perso la notifica).
Klaas,

Avevo bisogno di alson#pragma clang diagnostic ignored "-Wselector"
massimo

1
@mdorseif La maggior parte delle volte l'avviso che devi "escludere" è elencato nel registro di compilazione. Puoi silenziare qualsiasi avviso con questo concetto. Sono contento di aver aggiunto il tuo per quanto riguarda i selettori.
Klaas,

@epologee puoi fare lo stesso tramite l'impostazione build "Selettore non dichiarato"

194

Dai un'occhiata a NSSelectorFromString .

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

Ti consentirà di creare un selettore in fase di esecuzione, anziché in fase di compilazione tramite la @selectorparola chiave, e il compilatore non avrà alcuna possibilità di lamentarsi.


Ciao @sergio, entrambe le risposte di tuo e @ jacobrelkin funzionano. Praticamente presentato contemporaneamente. Mi aiuterai a scegliere la risposta "migliore", se ce n'è una?
epologo

2
Mi piace di più questa risposta perché sembra più "cacao" -y (?). L' sel_registerName()aspetto sembra oscuro e il tipo che non dovresti chiamare direttamente se non sai cosa stai facendo, un po 'come obj_msg_send ();)
Nicolas Miari

15
Non sono sicuro se si tratta di Xcode 5, ma sto ricevendo un avviso diverso con questa implementazione: "PerformSelector potrebbe causare una perdita perché il suo selettore è sconosciuto" .
Hampden123,

1
@ Hampden123: questo è un problema diverso. dai un'occhiata qui: stackoverflow.com/questions/7017281/…
sergio

52

Penso che ciò sia dovuto al fatto che per qualche strana ragione il selettore non è registrato con il runtime.

Prova a registrare il selettore tramite sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

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

Ciao @jacobrelkin, entrambe le risposte di tuo e @ sergio funzionano. Praticamente presentato contemporaneamente. Mi aiuterai a scegliere la risposta "migliore", se ce n'è una?
epologo

2
@epologee NSSelectorFromStringchiama sel_registerName()comunque sotto il cofano. Scegli quello che preferisci.
Jacob Relkin,

1
@epologee Penso che chiamare sel_registerName()direttamente sia più esplicito sul perché lo stai facendo. NSSelectorFromStringnon ti dice che tenterà di registrare il selettore.
Jacob Relkin,

8
Non sono sicuro se si tratta di Xcode 5, ma sto ricevendo un avviso diverso con questa implementazione: "PerformSelector potrebbe causare una perdita perché il suo selettore è sconosciuto" .
Hampden123,

@ Max_Power89 No. Vedi i miei altri commenti qui sotto. Non volevo passare troppo tempo su questo, quindi ho semplicemente incluso i file di intestazione.
Hampden123,

7

Ho ricevuto quel messaggio per andare via # includendo il file con il metodo. Nient'altro è stato usato da quel file.


Sebbene questa sia una soluzione meno aggraziata, funziona per me dato che ho i "sospetti noti" che potrebbero ricevere il selettore. Inoltre, se implemento l'approccio selettore di runtime, riceverei comunque un avviso diverso sull'istruzione performSelector; vale a dire "PerformSelector può causare una perdita perché il suo selettore è sconosciuto" . Quindi grazie!
Hampden123,

2
Nessuna delle risposte più votate è corretta. L'intento dell'avviso "selettore non dichiarato" è quello di rilevare gli errori in fase di compilazione se si modifica il nome del selettore su cui si basava. Quindi è più corretto importare il file che dichiara il metodo su cui si basava.
Brane,

7

Mi rendo conto di essere un po 'in ritardo su questo thread, ma per completezza, è possibile disattivare a livello globale questo avviso utilizzando le impostazioni di build di destinazione.

Nella sezione "Avvisi Apple LLVM - Obiettivo-C", modifica:

Undeclared Selector - NO

6

Se la tua classe implementa il metodo setError: (anche dichiarando dinamico il setter dell'eventuale proprietà dell'errore) potresti volerlo dichiarare nel tuo file di interfaccia (.h), o se non ti piace mostrarlo in quel modo potresti prova con il trucco complicato di PrivateMethods:

@interface Yourclass (PrivateMethods)

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

@end

appena prima della tua @implementazione, questo dovrebbe nascondere gli avvisi;).


Grazie, ma sto chiamando il metodo da una categoria, quindi questo non si applica. Saluti, EP.
epologo

E alcuni di noi stanno facendo cose che sono più esotiche: il selettore è implementato in un oggetto F #, nel mio caso.
James Moore,

1
Questo non elimina l'avviso in XCode 7.1.1 / iOS 9.1, posso vederePerformSelector may cause a leak because its selector is unknown
loretoparisi,

3

Una macro davvero comodo da mettere nella vostra .pcho Common.ho dove vuoi:

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

È una modifica di questa domanda per un problema simile ...


3

Puoi disattivarlo in Xcode come nello screenshot:

inserisci qui la descrizione dell'immagine


Ben fatto. Tuttavia, preferisco disabilitare l'avviso solo per casi espliciti, dicendo "clang è sbagliato in questa occasione, so cosa sto facendo". Grazie per il tuo contributo!
epologo il

2

È inoltre possibile eseguire prima il cast dell'oggetto in questione su un ID per evitare l'avvertimento:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}

1
Questo non elimina lo stesso avvertimento sul contenuto dell'espressione if, fino a XC7.1 fino ai giorni nostri.
Martin-Gilles Lavoie,

2

Un altro modo per evitare questo avviso è assicurarsi che il metodo di selezione sia simile al seguente:

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

Non dimenticare "mittente (id)" se si desidera accettare un mittente o specificare un tipo di oggetto mittente se si preferisce.


0

Mentre la risposta corretta sta probabilmente nell'informare Xcode attraverso le importazioni o registrando il selettore che esiste un tale selettore, nel mio caso mi mancava un punto e virgola. Assicurati prima di "correggere" l'errore che forse l'errore è corretto e il tuo codice non lo è. Ho trovato l'errore nell'esempio MVCNetworking di Apple, ad esempio.


No, la risposta corretta non è stata quella di informare Xcode attraverso le importazioni, poiché tali importazioni erano in atto. La risposta corretta era la risposta sopra che era contrassegnata come ... la risposta corretta, anche se la risposta di @ sergio avrebbe risolto anche il problema. L'uso del selettore errato non è l'oggetto di questa domanda, pertanto la modifica del selettore non è una risposta. Ti risparmierò comunque il downvote.
epologo il

1
Grazie per avermi ricordato che probabilmente avrei dovuto usare un commento. Tutto quello che posso dire è che le importazioni mancanti causano anche questo avviso Xcode, se non questa specifica istanza. Consiglierei NSSelectorFromString o altre opzioni di "registrazione" solo quando si crea un selettore in fase di esecuzione o si risponde alle chiamate di metodo in modo dinamico (ad esempio methodSignatureForSelector). Registrarlo significa che stai "aggirando l'errore" e quindi non è corretto per alcune circostanze, perché un approccio più corretto sarebbe quello di correggere l'avvertimento (se l'analisi del clang era corretta, cioè.)
Louis St-Amour

In effetti, ora vedo che la domanda originale dice chiaramente "senza la necessità di un protocollo implementato" - e non menziona affatto le importazioni. Vorrei quindi affermare che l'importazione della categoria stessa potrebbe essere l'opzione migliore per questo utente. Qualsiasi altra cosa qui potrebbe definire il selettore due volte, tecnicamente parlando. Sì? - Modifica: Ah, l'ho portato troppo lontano. Grazie per la tua risposta, mi fermo adesso. :)
Louis St-Amour,

-1

Sono stato in grado di ottenere l'avvertimento di andare via aggiungendo il metodo alloraothing (divulgazione: non ci ho pensato ma l'ho trovato cercando su Google timer programmato con intervallo di tempo)

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

}

Mentre apprezzo saper nascondere l'avvertimento, risolverlo è meglio e né le tecniche di Sergio né Relkin hanno funzionato per me, per motivi sconosciuti.


1
Se qualcun altro legge questa soluzione, che funzionerà , sarà piuttosto confuso, incluso il tuo sé futuro. Se sei sicuro di sapere cosa stai facendo chiamando un selettore inesistente, causando così un avviso, salta lo stub del metodo fuorviante e assicurati che il codice esprima le tue intenzioni.
epologo

1
Buon punto. Stavo lavorando con il codice ereditato e stavo solo cercando di capire come far sparire l'avvertimento, non cercando di risolvere la domanda di base sul perché avere un selettore inesistente. Un passo alla volta, dico sempre.
user938797
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.