我得到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")];
为什么我得到这个警告?我理解编译器不能检查选择器是否存在,但为什么会导致泄漏?我怎样才能改变我的代码,让我不再得到这个警告呢?
当前回答
如果你不需要传递任何参数,一个简单的解决方法是使用valueForKeyPath。这甚至可以在Class对象上实现。
NSString *colorName = @"brightPinkColor";
id uicolor = [UIColor class];
if ([uicolor respondsToSelector:NSSelectorFromString(colorName)]){
UIColor *brightPink = [uicolor valueForKeyPath:colorName];
...
}
其他回答
在Xcode 4.2的LLVM 3.0编译器中,你可以像下面这样抑制警告:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.ticketTarget performSelector: self.ticketAction withObject: self];
#pragma clang diagnostic pop
如果你在几个地方得到错误,并且想要使用C宏系统来隐藏指令,你可以定义一个宏来更容易地抑制警告:
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)
你可以像这样使用宏:
SuppressPerformSelectorLeakWarning(
[_target performSelector:_action withObject:self]
);
如果你需要执行消息的结果,你可以这样做:
id result;
SuppressPerformSelectorLeakWarning(
result = [_target performSelector:_action withObject:self]
);
要使用执行选择器忽略文件中的错误,添加一个#pragma,如下所示:
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
这将忽略这一行上的警告,但仍然允许它贯穿整个项目的其余部分。
您还可以在这里使用协议。因此,创建一个这样的协议:
@protocol MyProtocol
-(void)doSomethingWithObject:(id)object;
@end
在需要调用选择器的类中,有一个@属性。
@interface MyObject
@property (strong) id<MyProtocol> source;
@end
当你需要在MyObject的实例中调用@selector(doSomethingWithObject:)时,这样做:
[self.source doSomethingWithObject:object];
不要压制警告!
有不少于12个可选的解决方案来修补编译器。 虽然在第一次实现时你很聪明,但地球上很少有工程师能追随你的脚步,而这段代码最终会崩溃。
安全的路线:
所有这些解决方案都是可行的,只是在一定程度上与您的初衷有所不同。假设param可以为nil:
安全路线,相同的概念行为:
// GREAT
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:YES modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:YES modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
安全路线,行为稍有不同:
(见此回复) 使用任何线程代替[NSThread主线程]。
// GOOD
[_controller performSelector:selector withObject:anArgument afterDelay:0];
[_controller performSelector:selector withObject:anArgument afterDelay:0 inModes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO];
[_controller performSelectorOnMainThread:selector withObject:anArgument waitUntilDone:NO modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
[_controller performSelectorInBackground:selector withObject:anArgument];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO];
[_controller performSelector:selector onThread:[NSThread mainThread] withObject:anArgument waitUntilDone:NO modes:@[(__bridge NSString *)kCFRunLoopDefaultMode]];
危险的路线
需要某种编译器静音,这是必然的破坏。请注意,目前,它确实在Swift中中断了。
// AT YOUR OWN RISK
[_controller performSelector:selector];
[_controller performSelector:selector withObject:anArgument];
[_controller performSelector:selector withObject:anArgument withObject:nil];
在编译器允许覆盖警告之前,可以使用运行时作为一种变通方法。
你需要标题:
#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")];