我得到ARC编译器的以下警告:

"performSelector may cause a leak because its selector is unknown".

这是我正在做的:

[_controller performSelector:NSSelectorFromString(@"someMethod")];

为什么我得到这个警告?我理解编译器不能检查选择器是否存在,但为什么会导致泄漏?我怎样才能改变我的代码,让我不再得到这个警告呢?


当前回答

您还可以在这里使用协议。因此,创建一个这样的协议:

@protocol MyProtocol
-(void)doSomethingWithObject:(id)object;
@end

在需要调用选择器的类中,有一个@属性。

@interface MyObject
    @property (strong) id<MyProtocol> source;
@end

当你需要在MyObject的实例中调用@selector(doSomethingWithObject:)时,这样做:

[self.source doSomethingWithObject:object];

其他回答

在编译器允许覆盖警告之前,可以使用运行时作为一种变通方法。

你需要标题:

#import <objc/message.h>

然后试试下面的方法:

// For strict compilers.
((id(*)(id,SEL))objc_msgSend)(_controller, sel_getUid("someMethod"));

OR

// Old answer's code:
objc_msgSend(_controller, NSSelectorFromString(@"someMethod"));

而不是:

[_controller performSelector:NSSelectorFromString(@"someMethod")];

这段代码不涉及编译器标志或直接运行时调用:

SEL selector = @selector(zeroArgumentMethod);
NSMethodSignature *methodSig = [[self class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setSelector:selector];
[invocation setTarget:self];
[invocation invoke];

NSInvocation允许设置多个参数,因此与performSelector不同,这将适用于任何方法。

我的猜测是这样的:因为选择器对编译器是未知的,ARC不能强制执行适当的内存管理。

事实上,有时内存管理通过特定的约定与方法的名称绑定在一起。具体来说,我在考虑方便构造函数和make方法;前者按照约定返回一个自动释放的对象;后者是一个保留对象。该约定基于选择器的名称,因此如果编译器不知道选择器,那么它就不能强制执行正确的内存管理规则。

如果这是正确的,我认为你可以安全地使用你的代码,只要你确保内存管理一切正常(例如,你的方法不返回它们分配的对象)。

为了让Scott Thompson的宏更通用:

// String expander
#define MY_STRX(X) #X
#define MY_STR(X) MY_STRX(X)

#define MYSilenceWarning(FLAG, MACRO) \
_Pragma("clang diagnostic push") \
_Pragma(MY_STR(clang diagnostic ignored MY_STR(FLAG))) \
MACRO \
_Pragma("clang diagnostic pop")

然后这样使用它:

MYSilenceWarning(-Warc-performSelector-leaks,
[_target performSelector:_action withObject:self];
                )

您还可以在这里使用协议。因此,创建一个这样的协议:

@protocol MyProtocol
-(void)doSomethingWithObject:(id)object;
@end

在需要调用选择器的类中,有一个@属性。

@interface MyObject
    @property (strong) id<MyProtocol> source;
@end

当你需要在MyObject的实例中调用@selector(doSomethingWithObject:)时,这样做:

[self.source doSomethingWithObject:object];