167
votes

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?

Cheers,
EP

12
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.DawnSong

12 Answers

257
votes

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
200
votes

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.

53
votes

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]];
}
7
votes

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

7
votes

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
6
votes

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 ;).

3
votes

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...

3
votes

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

enter image description here

2
votes

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

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

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.

0
votes

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.

-1
votes

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.