166

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

epologee
  • 10,851
  • 9
  • 64
  • 102
  • 2
    The solution is well explained in [performSelector may cause a leak because its selector is unknown](http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown) – loretoparisi Nov 18 '15 at 12:01
  • 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 May 25 '19 at 12:51

12 Answers12

256

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
Klaas
  • 20,950
  • 10
  • 88
  • 98
  • `#pragma clang diagnostic push` `#pragma clang diagnostic ignored "-Wundeclared-selector"` `// Do your thing` `#pragma clang diagnostic pop` – dizy Aug 21 '13 at 03:50
  • yes, It does as @dizy states. (Sorry for the late answer, but I missed the notification). – Klaas Aug 21 '13 at 14:28
  • I alson needed `#pragma clang diagnostic ignored "-Wselector"` – max Sep 22 '13 at 15:14
  • 1
    @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. – Klaas Sep 22 '13 at 19:16
  • @epologee you can do the same via build setting "Undeclared Selector" –  Dec 21 '13 at 11:29
  • 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. – DawnSong May 25 '19 at 12:50
197

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.

T'n'E
  • 502
  • 4
  • 15
sergio
  • 67,961
  • 11
  • 98
  • 119
  • 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? – epologee Jun 03 '11 at 09:18
  • 2
    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() ;) – Nicolas Miari Sep 16 '13 at 15:18
  • 15
    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"_. – Hampden123 Nov 20 '13 at 16:38
  • 1
    @Hampden123: that is a different issue. have a look here: http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown/7017537#7017537 – sergio Nov 20 '13 at 17:50
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]];
}
Jacob Relkin
  • 151,673
  • 29
  • 336
  • 313
  • 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? – epologee Jun 03 '11 at 09:18
  • 3
    @epologee `NSSelectorFromString` calls `sel_registerName()` under the hood anyway. Choose whichever suits you better. – Jacob Relkin Jun 03 '11 at 09:24
  • 2
    @epologee 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. – Jacob Relkin Jun 03 '11 at 09:26
  • 8
    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"_. – Hampden123 Nov 20 '13 at 16:36
  • @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. – Hampden123 Nov 26 '13 at 16:53
  • 1
    @Hampden123 what you mentioned is another warning. [What you need](https://stackoverflow.com/a/7933931/1107242) might be `#pragma clang diagnostic ignored "-Warc-performSelector-leaks"` – DawnSong Nov 27 '20 at 07:04
7

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

Mark Patterson
  • 401
  • 2
  • 5
  • 14
  • 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! – Hampden123 Nov 20 '13 at 16:31
  • 2
    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. – Brane Feb 18 '14 at 16:04
7

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
Quixiote
  • 81
  • 1
  • 7
6

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

i_mush
  • 206
  • 2
  • 11
  • Thanks, but I'm calling the method from a category, so this does not apply. Cheers, EP. – epologee Jun 03 '11 at 10:06
  • And some of us are doing things that are more exotic - the selector is implemented in an F# object, in my case. – James Moore Oct 21 '13 at 23:00
  • 1
    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 unknown``` – loretoparisi Nov 18 '15 at 11:46
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...

Community
  • 1
  • 1
Aviel Gross
  • 8,880
  • 2
  • 47
  • 58
3

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

enter image description here

SmallChess
  • 7,103
  • 9
  • 49
  • 79
  • 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! – epologee Oct 12 '16 at 21:38
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];
}
Swindler
  • 722
  • 7
  • 8
2

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.

Repose
  • 1,633
  • 1
  • 17
  • 19
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.

Louis St-Amour
  • 3,885
  • 1
  • 25
  • 27
  • 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. – epologee Oct 23 '13 at 11:29
  • 1
    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.) – Louis St-Amour Oct 28 '13 at 19:47
  • 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. :) – Louis St-Amour Oct 28 '13 at 19:50
-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.

user938797
  • 127
  • 3
  • 14
  • 1
    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. – epologee Dec 07 '14 at 12:01
  • 1
    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. – user938797 Dec 08 '14 at 18:54