Comment se débarrasser de l'avertissement `` sélecteur non déclaré ''


162

Je souhaite utiliser un sélecteur sur une instance NSObject sans avoir besoin d'un protocole implémenté. Par exemple, il existe une méthode de catégorie qui devrait définir une propriété d'erreur si l'instance NSObject sur laquelle elle est appelée la prend en charge. Voici le code et le code fonctionne comme prévu:

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

Cependant, le compilateur ne voit aucune méthode avec la signature setError:, donc il me donne un avertissement, pour chaque ligne qui contient l' @selector(setError:)extrait de code:

Undeclared selector 'setError:'

Je ne veux pas avoir à déclarer un protocole pour me débarrasser de cet avertissement, car je ne veux pas que toutes les classes qui peuvent l'utiliser implémentent quelque chose de spécial. Juste par convention, je veux qu'ils aient une setError:méthode ou une propriété.

Est-ce faisable? Comment?

Salutations,
EP



Un sélecteur obsolète provoquera l'avertissement. Il n'est plus sûr d'accéder au sélecteur car le sélecteur peut être supprimé à un moment donné.
DawnSong

Réponses:


254

Une autre option serait de désactiver l'avertissement avec:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

Vous pouvez placer cette ligne dans le fichier .m où l'avertissement se produit.

Mettre à jour:

Cela fonctionne également avec LLVM comme ceci:

#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

oui, il fait ce que @dizy déclare. (Désolé pour la réponse tardive, mais j'ai manqué la notification).
Klaas

J'avais déjà besoin#pragma clang diagnostic ignored "-Wselector"
max

1
@mdorseif La plupart du temps, l'avertissement que vous «devez» exclure est répertorié dans le journal de compilation. Vous pouvez désactiver n'importe quel avertissement avec ce concept. Heureux d'avoir ajouté le vôtre concernant les sélecteurs.
Klaas

@epologee, vous pouvez faire la même chose via le paramètre de construction "Sélecteur non déclaré"

194

Jetez un œil à NSSelectorFromString .

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

Cela vous permettra de créer un sélecteur au moment de l'exécution, plutôt qu'au moment de la compilation via le @selectormot - clé, et le compilateur n'aura aucune chance de se plaindre.


Salut @sergio, vos réponses et celles de @ jacobrelkin fonctionnent. Presque soumis simultanément. M'aiderez-vous à choisir la «meilleure» réponse, s'il y en a une?
epologee

2
J'aime plus cette réponse car elle ressemble plus à "Cocoa" -y (?). Le sel_registerName()truc semble obscur et le genre que vous ne devriez pas appeler directement à moins de savoir ce que vous faites, un peu comme obj_msg_send ();)
Nicolas Miari

15
Je ne sais pas si c'est Xcode 5, mais j'obtiens un avertissement différent avec cette implémentation: "PerformSelector peut provoquer une fuite car son sélecteur est inconnu" .
Hampden123

1
@ Hampden123: c'est un problème différent. jetez un œil ici: stackoverflow.com/questions/7017281/...
sergio

52

Je pense que c'est parce que pour une raison étrange, le sélecteur n'est pas enregistré avec le runtime.

Essayez d'enregistrer le sélecteur via sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

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

Salut @jacobrelkin, vos réponses et celles de @ sergio fonctionnent. Presque soumis simultanément. M'aiderez-vous à choisir la «meilleure» réponse, s'il y en a une?
epologee

2
@epologee NSSelectorFromStringappelle quand même sel_registerName()sous le capot. Choisissez celui qui vous convient le mieux.
Jacob Relkin

1
@epologee Je pense qu'appeler sel_registerName()directement est plus explicite sur la raison pour laquelle vous le faites. NSSelectorFromStringne vous dit pas qu'il va tenter d'enregistrer le sélecteur.
Jacob Relkin

8
Je ne sais pas si c'est Xcode 5, mais j'obtiens un avertissement différent avec cette implémentation: "PerformSelector peut provoquer une fuite car son sélecteur est inconnu" .
Hampden123

@ Max_Power89 Non. Voir mes autres commentaires ci-dessous. Je ne voulais pas passer trop de temps là-dessus, alors j'ai simplement inclus les fichiers d'en-tête.
Hampden123

7

J'ai reçu ce message en # incluant le fichier avec la méthode. Rien d'autre n'a été utilisé à partir de ce fichier.


Bien que ce soit une solution moins gracieuse, cela fonctionne pour moi car j'ai les "suspects connus" qui pourraient recevoir le sélecteur. De plus, si j'implémente l'approche du sélecteur d'exécution, j'obtiendrais toujours un avertissement différent à l'instruction performSelector; à savoir, "PerformSelector peut provoquer une fuite car son sélecteur est inconnu" . Donc merci!
Hampden123

2
Aucune des réponses les plus votées n'est correcte. Le but de l'avertissement "sélecteur non déclaré" est de détecter les erreurs au moment de la compilation si vous modifiez le nom du sélecteur sur lequel vous vous êtes appuyé. Il est donc plus correct de #importer le fichier qui déclare la méthode sur laquelle vous vous êtes appuyé.
Brane

7

Je me rends compte que je suis un peu en retard dans ce fil, mais pour être complet, vous pouvez désactiver globalement cet avertissement en utilisant les paramètres de construction cible.

Dans la section 'Avertissements Apple LLVM - Objective-C', modifiez:

Undeclared Selector - NO

6

Si votre classe implémente la méthode setError: (même en déclarant dynamic le setter de la propriété d'erreur éventuelle), vous voudrez peut-être la déclarer dans votre fichier d'interface (.h), ou si vous n'aimez pas l'afficher de cette façon, vous pouvez essayez avec l'astuce délicate de PrivateMethods:

@interface Yourclass (PrivateMethods)

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

@end

juste avant votre @implementation, cela devrait masquer les avertissements;).


Merci, mais j'appelle la méthode à partir d'une catégorie, donc cela ne s'applique pas. Cheers, EP.
epologee

Et certains d'entre nous font des choses plus exotiques - le sélecteur est implémenté dans un objet F #, dans mon cas.
James Moore

1
Cela ne supprime pas l'avertissement dans XCode 7.1.1 / iOS 9.1, je peux voirPerformSelector may cause a leak because its selector is unknown
loretoparisi

3

Une macro vraiment confortable à mettre dans votre .pchou Common.hou où vous voulez:

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

C'est une modification de cette question pour un problème similaire ...


3

Vous pouvez le désactiver dans Xcode comme dans la capture d'écran:

entrez la description de l'image ici


Joli. Pourtant, je préfère désactiver l'avertissement uniquement pour les cas explicites, en disant "clang est faux dans cette occasion, je sais ce que je fais". Merci pour votre contribution!
epologee

2

Vous pouvez également convertir d'abord l'objet en question en un identifiant pour éviter l'avertissement:

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

1
Cela ne supprime pas le même avertissement sur le contenu de l'expression if, jusqu'à XC7.1 à ce jour.
Martin-Gilles Lavoie

2

Une autre façon d'éviter cet avertissement est de vous assurer que votre méthode de sélection ressemble à ceci:

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

N'oubliez pas "(id) sender" si vous voulez accepter un expéditeur ou spécifier un type d'objet expéditeur si vous préférez.


0

Alors que la bonne réponse consiste probablement à informer Xcode via des importations ou à enregistrer le sélecteur qu'un tel sélecteur existe, dans mon cas, il me manquait un point-virgule. Assurez-vous avant de «corriger» l'erreur que l'erreur est peut-être correcte et que votre code ne l'est pas. J'ai trouvé l'erreur dans l'exemple MVCNetworking d'Apple, par exemple.


Non, la bonne réponse n'était pas d'informer Xcode via les importations, car ces importations étaient en place. La bonne réponse était la réponse ci-dessus qui a été marquée comme ... la bonne réponse, bien que la réponse de @ sergio résoudrait également le problème. Utiliser le mauvais sélecteur n'est pas le sujet de cette question, donc changer le sélecteur n'est pas une réponse. Je vous épargnerai cependant le vote défavorable.
epologee

1
Merci de me rappeler que j'aurais probablement dû utiliser un commentaire. Tout ce que je peux dire, c'est que les importations manquantes provoquent également cet avertissement Xcode, sinon cette instance spécifique. Je recommanderais uniquement NSSelectorFromString ou d'autres options "d'enregistrement" lors de la construction d'un sélecteur à l'exécution ou de la réponse aux appels de méthode de manière dynamique (par exemple, methodSignatureForSelector). L'enregistrer signifie que vous «travaillez autour de l'erreur» et n'est donc pas correct dans certaines circonstances, car une approche plus correcte serait de corriger l'avertissement (si l'analyse du bruit était correcte, c'est-à-dire.)
Louis St-Amour

En fait, je vois maintenant que la question initiale dit clairement "sans la nécessité d'un protocole mis en œuvre" - et ne mentionne pas du tout les importations. Je dirais donc que l'importation de la catégorie elle-même pourrait être la meilleure option pour cet utilisateur. Tout autre élément ici pourrait définir le sélecteur deux fois, techniquement parlant. Oui? - Edit: Ah, j'ai poussé ça trop loin. Merci pour votre réponse, je vais m'arrêter maintenant. :)
Louis St-Amour

-1

J'ai pu faire disparaître l'avertissement en ajoutant aucune méthode (divulgation: je n'y ai pas pensé, mais je l'ai trouvée en recherchant sur google le timer avec intervalle de temps)

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

}

Bien que j'apprécie de savoir comment cacher l'avertissement, il est préférable de le réparer et ni les techniques de Sergio ni de Relkin n'ont fonctionné pour moi, pour des raisons inconnues.


1
Si quelqu'un d'autre lit cette solution, qui fonctionnera , il / elle sera assez confus, y compris votre futur moi. Si vous êtes sûr de savoir ce que vous faites en appelant un sélecteur inexistant, provoquant ainsi un avertissement, ignorez le stub de méthode trompeur et assurez-vous que votre code exprime votre intention.
epologee

1
Bon point. Je travaillais avec du code hérité et j'essayais simplement de comprendre comment faire disparaître l'avertissement, sans essayer de résoudre la question fondamentale de savoir pourquoi avoir un sélecteur inexistant. Une étape à la fois, je dis toujours.
user938797
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.