So entfernen Sie die Warnung "Nicht deklarierter Selektor"

164

Ich möchte einen Selektor für eine NSObject-Instanz verwenden, ohne dass ein implementiertes Protokoll erforderlich ist. Beispielsweise gibt es eine Kategoriemethode, die eine Fehlereigenschaft festlegen sollte, wenn die aufgerufene NSObject-Instanz dies unterstützt. Dies ist der Code, und der Code funktioniert wie beabsichtigt:

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

Der Compiler sieht jedoch keine Methode mit der setError: -Signatur und gibt mir daher eine Warnung für jede Zeile, die das @selector(setError:)Snippet enthält :

Undeclared selector 'setError:'

Ich möchte kein Protokoll deklarieren müssen, um diese Warnung zu entfernen, da ich nicht möchte, dass alle Klassen, die dies verwenden, etwas Besonderes implementieren. Nur durch Konvention möchte ich, dass sie eine setError:Methode oder Eigenschaft haben.

Ist das machbar? Wie?

Prost,
EP

Epologe
quelle
2
Die Lösung wird in performSelector
loretoparisi
Ein veralteter Selektor löst die Warnung aus. Es ist nicht mehr sicher, auf den Selektor zuzugreifen, da der Selektor möglicherweise irgendwann entfernt wird.
DawnSong

Antworten:

254

Eine andere Möglichkeit wäre, die Warnung zu deaktivieren mit:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

Sie können diese Zeile in die .m-Datei einfügen, in der die Warnung auftritt.

Aktualisieren:

Es funktioniert auch mit LLVM wie folgt:

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

... your code here ...

#pragma clang diagnostic pop
Klaas
quelle
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic pop
schwindlig
Ja, es funktioniert wie in @dizy angegeben. (Entschuldigung für die späte Antwort, aber ich habe die Benachrichtigung verpasst).
Klaas
Ich brauchte auch#pragma clang diagnostic ignored "-Wselector"
max
1
@mdorseif Meistens wird die Warnung, die Sie ausschließen müssen, im Kompilierungsprotokoll aufgeführt. Mit diesem Konzept können Sie jede Warnung stummschalten. Ich bin froh, dass Sie Ihre in Bezug auf Selektoren hinzugefügt haben.
Klaas
@epologee Sie können das gleiche über Build-Einstellung "Undeclared Selector"
195

Schauen Sie sich NSSelectorFromString an .

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

Damit können Sie zur Laufzeit einen Selektor erstellen, anstatt zur Kompilierungszeit über das @selectorSchlüsselwort, und der Compiler hat keine Möglichkeit, sich zu beschweren.

Sergio
quelle
Hallo @sergio, sowohl deine als auch @ jacobrelkins Antworten funktionieren. Ziemlich gleichzeitig eingereicht. Helfen Sie mir, die "bessere" Antwort zu finden, falls es eine gibt?
Epologe
2
Ich mag diese Antwort mehr, weil sie mehr "Cocoa" -y (?) Sieht. Das sel_registerName()Ding sieht dunkel aus und die Art, die Sie nicht direkt anrufen sollten, wenn Sie nicht wissen, was Sie tun, wie obj_msg_send ();)
Nicolas Miari
15
Nicht sicher , ob es Xcode ist 5, aber ich bin eine andere Warnung mit dieser Implementierung bekommen: „perform kann ein Leck verursachen , weil seine Wähler sind unbekannt“ .
Hampden123
1
@ Hampden123: das ist ein anderes Problem. Schauen Sie hier: stackoverflow.com/questions/7017281/…
Sergio
52

Ich denke, das liegt daran, dass der Selektor aus irgendeinem Grund nicht zur Laufzeit registriert ist.

Versuchen Sie, den Selektor über Folgendes zu registrieren sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}
Jacob Relkin
quelle
Hallo @jacobrelkin, sowohl deine als auch @ sergios Antworten funktionieren. Ziemlich gleichzeitig eingereicht. Helfen Sie mir, die "bessere" Antwort zu finden, falls es eine gibt?
Epologe
2
@epologee NSSelectorFromStringruft sel_registerName()sowieso unter der Haube an. Wählen Sie, was besser zu Ihnen passt.
Jacob Relkin
1
@epologee Ich denke, dass ein sel_registerName()direkter Anruf expliziter darüber ist, warum Sie das tun. sagt Ihnen NSSelectorFromStringnicht , dass versucht wird, den Selektor zu registrieren.
Jacob Relkin
8
Nicht sicher , ob es Xcode ist 5, aber ich bin eine andere Warnung mit dieser Implementierung bekommen: „perform kann ein Leck verursachen , weil seine Wähler sind unbekannt“ .
Hampden123
@ Max_Power89 Nein. Siehe meine anderen Kommentare unten. Ich wollte nicht zu viel Zeit damit verbringen, also habe ich einfach die Header-Dateien eingefügt.
Hampden123
7

Ich habe diese Nachricht erhalten, indem ich die Datei mit der Methode eingeschlossen habe. Aus dieser Datei wurde nichts anderes verwendet.

Mark Patterson
quelle
Dies ist zwar eine weniger anmutige Lösung, funktioniert aber für mich, da ich die "bekannten Verdächtigen" habe, die möglicherweise den Selektor erhalten. Wenn ich den Runtime Selector-Ansatz implementiere, wird bei der performSelector-Anweisung immer noch eine andere Warnung angezeigt. nämlich „perform kann ein Leck verursachen , weil seine Wähler nicht bekannt ist“ . So danke!
Hampden123
2
Keine der am besten bewerteten Antworten ist richtig. Mit der Warnung "Nicht deklarierter Selektor" sollen Fehler beim Kompilieren abgefangen werden, wenn Sie den Namen des Selektors ändern, auf den Sie sich verlassen. Daher ist es am richtigsten, # die Datei zu importieren, die die Methode deklariert, auf die Sie sich verlassen haben.
Brane
7

Mir ist klar, dass ich zu spät zu diesem Thread komme, aber der Vollständigkeit halber können Sie diese Warnung mithilfe der Ziel-Build-Einstellungen global deaktivieren.

Im Abschnitt "Apple LLVM-Warnungen - Ziel-C" ändern Sie:

Undeclared Selector - NO
Quixiote
quelle
6

Wenn Ihre Klasse die setError: -Methode implementiert (auch wenn Sie den Setter der eventuellen Fehlereigenschaft dynamisch deklarieren), möchten Sie sie möglicherweise in Ihrer Schnittstellendatei (.h) deklarieren, oder wenn Sie sie nicht so anzeigen möchten, können Sie dies Versuchen Sie es mit dem kniffligen Trick von PrivateMethods:

@interface Yourclass (PrivateMethods)

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

@end

kurz vor deiner @implementierung sollte dies die Warnungen verbergen;).

i_mush
quelle
Danke, aber ich rufe die Methode aus einer Kategorie auf, daher gilt dies nicht. Prost, EP.
Epologe
Und einige von uns machen Dinge, die exotischer sind - der Selektor ist in meinem Fall in einem F # -Objekt implementiert.
James Moore
1
Dies wird die Warnung in XCode 7.1.1 / iOS 9.1 nicht los, ich kann sehenPerformSelector may cause a leak because its selector is unknown
loretoparisi
3

Ein wirklich komfortables Makro, das Sie in Ihr .pchoder Common.hoder wo immer Sie möchten einfügen können:

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

Es ist eine Bearbeitung dieser Frage für ein ähnliches Problem ...

Aviel Gross
quelle
3

Sie können es in Xcode wie im Screenshot deaktivieren:

Geben Sie hier die Bildbeschreibung ein

SmallChess
quelle
Schön. Trotzdem ziehe ich es vor, die Warnung nur für explizite Fälle zu deaktivieren, indem ich sage "Klirren ist bei dieser Gelegenheit falsch, ich weiß, was ich tue". Danke für deinen Beitrag!
Epologee
2

Sie können das betreffende Objekt auch zuerst in eine ID umwandeln, um die Warnung zu vermeiden:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}
Schwindler
quelle
1
Dadurch wird die gleiche Warnung für den Inhalt des if-Ausdrucks bis XC7.1 bis heute nicht entfernt.
Martin-Gilles Lavoie
2

Eine andere Möglichkeit, diese Warnung zu vermeiden, besteht darin, sicherzustellen, dass Ihre Auswahlmethode folgendermaßen aussieht:

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

Vergessen Sie nicht "(id) Absender", wenn Sie einen Absender akzeptieren oder einen Typ eines Absenderobjekts angeben möchten, wenn Sie dies bevorzugen.

Ruhe
quelle
0

Während die richtige Antwort wahrscheinlich darin besteht, Xcode durch Importe zu informieren oder den Selektor zu registrieren, dass ein solcher Selektor existiert, fehlte mir in meinem Fall ein Semikolon. Stellen Sie sicher, bevor Sie den Fehler "beheben", dass der Fehler möglicherweise korrekt ist und Ihr Code nicht. Ich habe den Fehler zum Beispiel im MVCNetworking-Beispiel von Apple gefunden.

Louis St-Amour
quelle
Nein, die richtige Antwort bestand nicht darin, Xcode durch Importe zu informieren, da diese Importe vorhanden waren. Die richtige Antwort war die Antwort oben, die als ... die richtige Antwort markiert war, obwohl die Antwort von @ sergio auch das Problem lösen würde. Die Verwendung des falschen Selektors ist nicht Gegenstand dieser Frage, daher ist das Ändern des Selektors keine Antwort. Ich werde dir aber die Abwertung ersparen.
Epologee
1
Vielen Dank, dass Sie mich daran erinnert haben, dass ich wahrscheinlich einen Kommentar hätte verwenden sollen. Ich kann nur sagen, dass fehlende Importe auch diese Xcode-Warnung verursachen, wenn nicht diese bestimmte Instanz. Ich würde NSSelectorFromString oder andere solche "Registrierungs" -Optionen nur empfehlen, wenn zur Laufzeit ein Selektor erstellt oder auf Methodenaufrufe dynamisch reagiert wird (z. B. methodSignatureForSelector). Wenn Sie es registrieren, bedeutet dies, dass Sie "den Fehler umgehen" und daher unter bestimmten Umständen nicht korrekt sind, da ein korrekterer Ansatz darin besteht, die Warnung zu korrigieren (wenn die Klirranalyse korrekt war, das heißt)
Louis St-Amour
Tatsächlich sehe ich jetzt, dass die ursprüngliche Frage eindeutig "ohne die Notwendigkeit eines implementierten Protokolls" lautet - und Importe überhaupt nicht erwähnt. Daher würde ich darauf hinweisen, dass der Import der Kategorie selbst die beste Option für diesen Benutzer sein könnte. Alles andere hier könnte den Selektor technisch gesehen zweimal definieren. Ja? - Edit: Ah, ich bin zu weit gegangen. Vielen Dank für Ihre Antwort, ich werde jetzt aufhören. :)
Louis St-Amour
-1

Ich konnte die Warnung durch Hinzufügen einer anderen Methode zum Verschwinden bringen (Offenlegung: Ich habe nicht daran gedacht, sondern sie gefunden, indem ich auf den geplanten Termin mit dem Intervall gegoogelt habe)

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

}

Obwohl ich es zu schätzen weiß, wie man die Warnung verbirgt, ist es besser, sie zu beheben, und aus unbekannten Gründen haben weder Sergios noch Relkins Techniken für mich funktioniert.

user938797
quelle
1
Wenn jemand anderes diese Lösung liest, die funktionieren wird , ist er / sie ziemlich verwirrt, einschließlich Ihres zukünftigen Selbst. Wenn Sie sicher sind, dass Sie wissen, was Sie tun, indem Sie einen nicht vorhandenen Selektor aufrufen und dadurch eine Warnung auslösen, überspringen Sie den irreführenden Methodenstub und stellen Sie sicher, dass Ihr Code Ihre Absicht ausdrückt.
Epologee
1
Guter Punkt. Ich habe mit geerbtem Code gearbeitet und nur versucht herauszufinden, wie die Warnung verschwinden kann, und nicht versucht, die grundlegende Frage zu lösen, warum es keinen nicht vorhandenen Selektor gibt. Schritt für Schritt, sage ich immer.
user938797