How to get rid of the 'undeclared selector' warning
Asked Answered
C

12

170

I want to use a selector on an NSObject instance without the need for an implemented protocol. For example, there's a category method that should set an error property if the NSObject instance it's called on supports it. This is the code, and the code works as intended:

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

However, the compiler doesn't see any method around with the setError: signature, so it gives me a warning, for each line that contains the @selector(setError:) snippet:

Undeclared selector 'setError:'

I don't want to have to declare a protocol to get rid of this warning, because I don't want all classes that may use this to implement anything special. Just by convention I want them to have a setError: method or property.

Is this doable? How?

Catalpa answered 3/6, 2011 at 8:57 Comment(2)
The solution is well explained in performSelector may cause a leak because its selector is unknownMimetic
A deprecated selector will cause the warning. It's not safe to access the selector any more because the selector might be removed at some time.Censorious
F
261

Another option would be to disable the warning with:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

You can place this line in the .m file where the warning occurs.

Update:

It works also with LLVM like this:

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

... your code here ...

#pragma clang diagnostic pop
Fruge answered 8/5, 2012 at 23:50 Comment(6)
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" // Do your thing #pragma clang diagnostic popGangland
yes, It does as @Gangland states. (Sorry for the late answer, but I missed the notification).Fruge
I alson needed #pragma clang diagnostic ignored "-Wselector"Saran
@mdorseif Most of the time the warning that you 'have to' exclude is listed in the compile log. You can mute any warning with this concept. Glad you added yours regarding selectors.Fruge
@Catalpa you can do the same via build setting "Undeclared Selector"Spinks
A deprecated selector will cause the warning since it might be removed at some time. So it not safe to access the selector any more.Censorious
M
202

Have a look at NSSelectorFromString.

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

It will allow you to create a selector at runtime, instead of at compile time through the @selector keyword, and the compiler will have no chance to complain.

Mandiemandingo answered 3/6, 2011 at 9:2 Comment(4)
Hi @sergio, both your and @jacobrelkin's answers work. Pretty much submitted simultaneously. Will you help me pick the 'better' answer, if there is one?Catalpa
I like this answer more because it looks more "Cocoa"-y (?). The sel_registerName() thingy looks obscure and the kind you shouldn't be calling directly unless you know what you're doing, kinda like obj_msg_send() ;)Nonstandard
Not sure if it's Xcode 5, but I'm getting a different warning with this implementation: "PerformSelector may cause a leak because its selector is unknown".Wardieu
@Hampden123: that is a different issue. have a look here: #7017781Mandiemandingo
M
53

I think this is because for some odd reason the selector isn't registered with the runtime.

Try registering the selector via sel_registerName():

SEL setErrorSelector = sel_registerName("setError:");

if([self respondsToSelector:setErrorSelector]) {
   [self performSelector:setErrorSelector withObject:[NSError errorWithDomain:@"SomeDomain" code:1 userInfo:nil]];
}
Mccall answered 3/6, 2011 at 9:3 Comment(6)
Hi @jacobrelkin, both your and @sergio's answers work. Pretty much submitted simultaneously. Will you help me pick the 'better' answer, if there is one?Catalpa
@Catalpa NSSelectorFromString calls sel_registerName() under the hood anyway. Choose whichever suits you better.Mccall
@Catalpa I think that calling sel_registerName() directly is more explicit about why you're doing it. NSSelectorFromString doesn't tell you that it's going to attempt to register the selector.Mccall
Not sure if it's Xcode 5, but I'm getting a different warning with this implementation: "PerformSelector may cause a leak because its selector is unknown".Wardieu
@Max_Power89 No. See my other comments below. I didn't want to spend too much time on this so I simply included the header files.Wardieu
@Wardieu what you mentioned is another warning. What you need might be #pragma clang diagnostic ignored "-Warc-performSelector-leaks"Censorious
D
8

I realise I'm a bit late to this thread but for completeness, you can globally turn off this warning using the target build settings.

In section, 'Apple LLVM warnings - Objective-C', change:

Undeclared Selector - NO
Dellinger answered 30/4, 2015 at 9:45 Comment(0)
P
7

If your class implements the setError: method (even by declaring dynamic the setter of the eventual error property) you might want to declare it in your interface file ( .h), or if you don't like to show it that way you could try with the PrivateMethods tricky trick:

@interface Yourclass (PrivateMethods)

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

@end

just before your @implementation , this should hide the warnings ;).

Preparedness answered 3/6, 2011 at 9:25 Comment(3)
Thanks, but I'm calling the method from a category, so this does not apply. Cheers, EP.Catalpa
And some of us are doing things that are more exotic - the selector is implemented in an F# object, in my case.Heterogenesis
This does not get rid of the warning in XCode 7.1.1 / iOS 9.1, I can see PerformSelector may cause a leak because its selector is unknownMimetic
C
7

I got that message to go away by #include'ing the file with the method. Nothing else was used from that file.

Catherine answered 5/11, 2013 at 4:31 Comment(2)
While this is less graceful solution, it works for me as I have the "known suspects" who might be receiving the selector. Also, if I implement the runtime selector approach, I'd still get a different warning at the performSelector statement; namely, "PerformSelector may cause a leak because its selector is unknown". So thanks!Wardieu
Neither of the top voted answers is correct. The intent of the "undeclared selector" warning is to catch errors at compile time if you change the name of the selector you were relying on. So it is most correct to #import the file that declares the method you were relying on.Moxley
F
4

Another way to avoid this warning is to make sure your selector method looks like this:

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

Don't forget "(id) sender" if you want to accept any sender or specify a type of a sender object if you prefer.

Fou answered 14/11, 2018 at 14:58 Comment(0)
M
3

A really comfortable macro to put in your .pch or Common.h or wherever you want:

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

It's an edit of this question for similar issue...

Marconigraph answered 13/4, 2014 at 13:9 Comment(0)
S
3

You can turn it off in Xcode like in the screenshot:

enter image description here

Surmullet answered 11/10, 2016 at 4:11 Comment(1)
Nice one. Still, I prefer disabling the warning only for explicit cases, by means of saying "clang is wrong in this occasion, I know what I'm doing". Thanks for your input!Catalpa
N
2

You can also cast the object in question to an id first to avoid the warning:

if ([object respondsToSelector:@selector(myMethod)]) {
    [(id)object myMethod];
}
Nanna answered 5/8, 2014 at 7:46 Comment(1)
This doesn't get rid of the same warning on the if expression content, all the way up to XC7.1 to this day.Quinte
L
0

While the correct answer likely lies in informing Xcode through imports or registering the selector that such a selector exists, in my case I was missing a semi-colon. Make sure before you "fix" the error that perhaps, the error is correct and your code isn't. I found the error in Apple's MVCNetworking sample, for instance.

Leniency answered 22/10, 2013 at 18:20 Comment(3)
No, the correct answer was not in informing Xcode through imports, because those imports were in place. The correct answer was the answer above that was marked as ... the correct answer, although @sergio's answer would also solve the issue. Using the wrong selector is not the subject of this question, therefore changing the selector is not an answer. I'll save you the downvote though.Catalpa
Thanks for reminding me that I probably should have used a comment. All I can say is that missing imports also cause this Xcode warning, if not this specific instance. I would only recommend NSSelectorFromString or other such "registration" options when building a selector at runtime or responding to method calls in a dynamic fashion (e.g. methodSignatureForSelector). Registering it means you're "working around the error" and so isn't correct for some circumstances, because a more correct approach would be to fix the warning (if the clang analysis was correct, that is.)Leniency
In fact, I now see that the original question clearly says, "without the need for an implemented protocol" -- and does not mention imports at all. So I would put forth that importing the category itself might be the best option for this user. Anything else here could define the selector twice, technically speaking. Yes? -- Edit: Ah, I've taken this too far. Thanks for your response, I'll stop now. :)Leniency
L
-1

I was able to get the warning to go away by adding thenothing method (disclosure: I didn't think of this but found it by googling on scheduledtimerwithtimeinterval)

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

}

While I appreciate knowing how to hide the warning, fixing it is better and neither Sergio's nor Relkin's techniques worked for me, for unknown reasons.

Lawrence answered 28/11, 2014 at 21:41 Comment(2)
If someone else reads this solution, which will work, he/she will be pretty confused, including your future self. If you're sure you know what you're doing by calling a non-existing selector, thereby causing a warning, skip the misleading method stub and ensure your code expresses your intent.Catalpa
Good point. I was working with inherited code and just trying to figure out how to make the warning go away, not trying to solve the basic question of why have a non-existent selector. One step at a time, I always say.Lawrence

© 2022 - 2024 — McMap. All rights reserved.