如何摆脱“未声明选择器”警告


162

我想在NSObject实例上使用选择器,而不需要已实现的协议。例如,如果要调用的NSObject实例支持它,则有一个category方法应该设置一个error属性。这是代码,并且代码按预期工作:

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

但是,编译器看不到带有setError:签名的任何方法,因此对于包含该@selector(setError:)代码段的每一行,它都会向我发出警告:

Undeclared selector 'setError:'

我不想声明一个协议来摆脱此警告,因为我不希望所有可能使用此协议的类实现任何特殊的东西。按照约定,我希望它们具有setError:方法或属性。

这可行吗?怎么样?


EP,干杯



不建议使用的选择器将引起警告。再也不可以访问选择器了,因为选择器有时可能会被删除。
DawnSong

Answers:


254

另一种选择是通过以下方式禁用警告:

#pragma GCC diagnostic ignored "-Wundeclared-selector"

您可以将此行放置在发生警告的.m文件中。

更新:

它也可以与LLVM一起使用,如下所示:

#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

是的,它就像@dizy状态一样。(很抱歉,您的回答很晚,但是我错过了通知)。
克拉斯

我也需要#pragma clang diagnostic ignored "-Wselector"
最多

1
@mdorseif大多数情况下,“必须”排除的警告会在编译日志中列出。您可以使用此概念忽略任何警告。很高兴您添加了有关选择器的信息。
克拉斯

@epologee,您可以通过构建设置“ Undeclared Selector”执行相同的操作

194

看看NSSelectorFromString

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

它将允许您在运行时创建选择器,而不是在编译时通过@selector关键字创建选择器,并且编译器将没有机会抱怨。


@sergio,您好,您和@jacobrelkin的答案都可以。几乎同时提交。如果有答案,您会帮助我选择“更好”的答案吗?
epologee 2011年

2
我更喜欢这个答案,因为它看起来更像“可可” -y(?)。这sel_registerName()东西看上去晦涩难懂,除非您知道自己在做什么,否则不应该直接打电话给它,就像obj_msg_send();)
Nicolas Miari 2013年

15
不知道它是否为Xcode 5,但是在此实现中我收到了另一个警告:“ PerformSelector可能会导致泄漏,因为其选择器未知”
Hampden123

1
@ Hampden123:这是一个不同的问题。看看这里:stackoverflow.com/questions/7017281/...
塞尔吉奥

52

我认为这是因为出于某些奇怪的原因,选择器未在运行时注册。

尝试通过sel_registerName()以下方式注册选择器:

SEL setErrorSelector = sel_registerName("setError:");

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

@jacobrelkin,您好,您和@sergio的答案都可以。几乎同时提交。如果有答案,您会帮助我选择“更好”的答案吗?
epologee 2011年

2
无论如何,@ epologee会在后台进行NSSelectorFromString呼叫sel_registerName()。选择更适合您的。
Jacob Relkin 2011年

1
@epologee我认为sel_registerName()直接致电会更明确地说明您这样做的原因。NSSelectorFromString不会告诉您它将尝试注册选择器。
Jacob Relkin 2011年

8
不知道它是否为Xcode 5,但是在此实现中我收到了另一个警告:“ PerformSelector可能会导致泄漏,因为其选择器未知”
Hampden123

@ Max_Power89不。请参阅下面的其他评论。我不想花太多时间在上面,所以我只包含了头文件。
Hampden123

7

通过使用方法#include'文件,我得到了该消息。该文件未使用任何其他内容。


尽管这不是一个比较合适的解决方案,但是它对我有用,因为我有可能接收选择器的“已知嫌疑人”。另外,如果我实现了运行时选择器方法,则仍然会在performSelector语句中收到不同的警告;即,“ PerformSelector可能会导致泄漏,因为其选择器未知”。那谢谢啦!
Hampden123

2
票数最高的答案均不正确。“未声明选择器”警告的目的是,如果您更改所依赖的选择器的名称,则会在编译时捕获错误。因此,最正确的方法是#import声明您所依赖的方法的文件。
Brane 2014年

7

我知道我对此线程有些迟了,但是为了完整起见,您可以使用目标构建设置来全局关闭此警告。

在“ Apple LLVM警告-Objective-C”部分中,更改:

Undeclared Selector - NO

6

如果您的类实现了setError:方法(即使通过动态声明最终错误属性的设置器),则可能需要在接口文件(.h)中声明它,或者如果您不希望以这种方式显示它,则可以尝试使用PrivateMethods技巧:

@interface Yourclass (PrivateMethods)

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

@end

就在您的@implementation之前,这应该隐藏警告;)。


谢谢,但是我从类别中调用该方法,因此这并不适用。EP,干杯。
epologee 2011年

而且我们中有些人所做的事情更加奇特-就我而言,选择器是在F#对象中实现的。
詹姆斯·摩尔

1
这并不能消除XCode 7.1.1 / iOS 9.1中的警告,我可以看到PerformSelector may cause a leak because its selector is unknown
loretoparisi

3

一个非常舒适的宏,可以放入您.pchCommon.h您想要的任何地方:

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

这是类似问题的对此问题的编辑...


3

您可以在Xcode中将其关闭,如屏幕截图所示:

在此处输入图片说明


好一个。尽管如此,我还是喜欢只对明确的情况禁用警告,例如说““语在这种情况下是错误的,我知道我在做什么”。感谢您的输入!
epologee

2

您也可以先将相关对象强制转换为ID,以避免出现警告:

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

1
直到今天,对于if表达式内容,这一直都没有摆脱相同的警告。
Martin-Gilles Lavoie 2015年

2

避免此警告的另一种方法是确保选择器方法如下所示:

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

如果您想接受任何发件人,或者如果愿意,可以指定一个发件人对象的类型,请不要忘记“(id)sender”。


0

正确的答案可能在于通过导入来通知Xcode或将选择器注册为选择器,但就我而言,我缺少了分号。在“修复”错误之前,请确保该错误是正确的,而您的代码则不是。例如,我在Apple的MVCNetworking示例中发现了错误。


不,正确的答案不是通过导入通知Xcode,因为这些导入已经到位。正确答案是上面被标记为...正确答案的答案,尽管@sergio的答案也可以解决问题。使用错误的选择器不是这个问题的主题,因此更改选择器不是答案。不过,我会省掉你的票数。
epologee

1
感谢您提醒我,我可能应该使用评论。我只能说丢失的导入也会导致此Xcode警告,如果不是此特定实例的话。在运行时构建选择器或以动态方式响应方法调用(例如methodSignatureForSelector)时,我只建议使用NSSelectorFromString或其他此类“注册”选项。注册它意味着您正在“解决错误”,因此在某些情况下是不正确的,因为更正确的方法是修复警告(如果if语分析正确,那就是。)
Louis St-Amour

实际上,我现在看到原始问题清楚地表明:“不需要已实施的协议”,并且根本没有提到进口。因此,我认为导入类别本身可能是此用户的最佳选择。从技术上讲,这里的任何其他内容都可以两次定义选择器。是?-编辑:啊,我走得太远了。感谢您的回复,我现在停止。:)
路易·圣爱慕

-1

我可以通过添加thethothing方法来消除警告(公开:我没有想到这一点,但通过在scheduletimerwithtimeinterval上进行谷歌搜索找到了它)

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

}

尽管我很高兴知道如何隐藏该警告,但将其修复会更好,并且出于未知原因,Sergio和Relkin的技术都不适合我。


1
如果其他人阅读了此解决方案,该解决方案将起作用,那么他/她将非常困惑,包括您的未来自我。如果确定通过调用不存在的选择器知道自己在做什么,从而引起警告,请跳过具有误导性的方法存根,并确保您的代码表达了您的意图。
epologee 2014年

1
好点子。我正在处理继承的代码,只是想弄清楚如何使警告消失,而不是试图解决为什么选择器不存在的基本问题。我总是说一次。
user938797 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.