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

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

这是我正在做的:

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

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


当前回答

Matt Galloway的回答解释了原因:

考虑以下几点: id anotherObject1 = [someObject performSelector:@selector(copy)]; id anotherObject2 = [someObject performSelector:@selector(giveMeAnotherNonRetainedObject)]; 现在,ARC如何知道第一个返回一个保留计数为1的对象,而第二个 返回一个自动释放的对象?

如果您忽略了返回值,那么抑制警告似乎通常是安全的。如果你真的需要从performSelector获取一个保留对象,我不确定最好的做法是什么——除了“不要这样做”。

其他回答

因为您正在使用ARC,所以必须使用iOS 4.0或更高版本。这意味着你可以使用积木。如果不是记住要执行的选择器,而是取一个块,ARC将能够更好地跟踪实际发生的事情,并且不必冒意外引入内存泄漏的风险。

为了子孙后代着想,我决定参加竞选:)

最近,我看到越来越多的重构偏离了目标/选择器范式,而倾向于协议、块等。然而,我已经使用过几次的performSelector有一个替代物:

[NSApp sendAction: NSSelectorFromString(@"someMethod") to: _controller from: nil];

这些似乎是一个干净的,弧安全的,和几乎相同的替代performSelector没有太多的objc_msgSend()。

不过,我不知道iOS上是否有类似的功能。

如果你不需要传递任何参数,一个简单的解决方法是使用valueForKeyPath。这甚至可以在Class对象上实现。

NSString *colorName = @"brightPinkColor";
id uicolor = [UIColor class];
if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
    UIColor *brightPink = [uicolor valueForKeyPath:colorName];
    ...
}

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

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

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

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

你需要标题:

#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")];