我得到ARC编译器的以下警告:
"performSelector may cause a leak because its selector is unknown".
这是我正在做的:
[_controller performSelector:NSSelectorFromString(@"someMethod")];
为什么我得到这个警告?我理解编译器不能检查选择器是否存在,但为什么会导致泄漏?我怎样才能改变我的代码,让我不再得到这个警告呢?
我得到ARC编译器的以下警告:
"performSelector may cause a leak because its selector is unknown".
这是我正在做的:
[_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不同,这将适用于任何方法。
其他回答
为了子孙后代着想,我决定参加竞选:)
最近,我看到越来越多的重构偏离了目标/选择器范式,而倾向于协议、块等。然而,我已经使用过几次的performSelector有一个替代物:
[NSApp sendAction: NSSelectorFromString(@"someMethod") to: _controller from: nil];
这些似乎是一个干净的,弧安全的,和几乎相同的替代performSelector没有太多的objc_msgSend()。
不过,我不知道iOS上是否有类似的功能。
我的猜测是这样的:因为选择器对编译器是未知的,ARC不能强制执行适当的内存管理。
事实上,有时内存管理通过特定的约定与方法的名称绑定在一起。具体来说,我在考虑方便构造函数和make方法;前者按照约定返回一个自动释放的对象;后者是一个保留对象。该约定基于选择器的名称,因此如果编译器不知道选择器,那么它就不能强制执行正确的内存管理规则。
如果这是正确的,我认为你可以安全地使用你的代码,只要你确保内存管理一切正常(例如,你的方法不返回它们分配的对象)。
奇怪但事实是:如果可以接受(即result为void并且你不介意让runloop循环一次),添加一个延迟,即使这是零:
[_controller performSelector:NSSelectorFromString(@"someMethod")
withObject:nil
afterDelay:0];
这删除了警告,大概是因为它向编译器保证没有对象可以返回,并且在某种程度上管理不当。
我没有使用块方法,这给我带来了一些问题:
IMP imp = [_controller methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
我将使用NSInvocation,像这样:
-(void) sendSelectorToDelegate:(SEL) selector withSender:(UIButton *)button
if ([delegate respondsToSelector:selector])
{
NSMethodSignature * methodSignature = [[delegate class]
instanceMethodSignatureForSelector:selector];
NSInvocation * delegateInvocation = [NSInvocation
invocationWithMethodSignature:methodSignature];
[delegateInvocation setSelector:selector];
[delegateInvocation setTarget:delegate];
// remember the first two parameter are cmd and self
[delegateInvocation setArgument:&button atIndex:2];
[delegateInvocation invoke];
}
如果你不需要传递任何参数,一个简单的解决方法是使用valueForKeyPath。这甚至可以在Class对象上实现。
NSString *colorName = @"brightPinkColor";
id uicolor = [UIColor class];
if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
UIColor *brightPink = [uicolor valueForKeyPath:colorName];
...
}