我得到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")];
为什么我得到这个警告?我理解编译器不能检查选择器是否存在,但为什么会导致泄漏?我怎样才能改变我的代码,让我不再得到这个警告呢?
当前回答
我没有使用块方法,这给我带来了一些问题:
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];
}
其他回答
要使用执行选择器忽略文件中的错误,添加一个#pragma,如下所示:
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
这将忽略这一行上的警告,但仍然允许它贯穿整个项目的其余部分。
因为您正在使用ARC,所以必须使用iOS 4.0或更高版本。这意味着你可以使用积木。如果不是记住要执行的选择器,而是取一个块,ARC将能够更好地跟踪实际发生的事情,并且不必冒意外引入内存泄漏的风险。
@c-road在这里提供了正确的问题描述链接。下面您可以看到我的示例,当performSelector导致内存泄漏时。
@interface Dummy : NSObject <NSCopying>
@end
@implementation Dummy
- (id)copyWithZone:(NSZone *)zone {
return [[Dummy alloc] init];
}
- (id)clone {
return [[Dummy alloc] init];
}
@end
void CopyDummy(Dummy *dummy) {
__unused Dummy *dummyClone = [dummy copy];
}
void CloneDummy(Dummy *dummy) {
__unused Dummy *dummyClone = [dummy clone];
}
void CopyDummyWithLeak(Dummy *dummy, SEL copySelector) {
__unused Dummy *dummyClone = [dummy performSelector:copySelector];
}
void CloneDummyWithoutLeak(Dummy *dummy, SEL cloneSelector) {
__unused Dummy *dummyClone = [dummy performSelector:cloneSelector];
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Dummy *dummy = [[Dummy alloc] init];
for (;;) { @autoreleasepool {
//CopyDummy(dummy);
//CloneDummy(dummy);
//CloneDummyWithoutLeak(dummy, @selector(clone));
CopyDummyWithLeak(dummy, @selector(copy));
[NSThread sleepForTimeInterval:1];
}}
}
return 0;
}
在我的例子中,唯一导致内存泄漏的方法是CopyDummyWithLeak。原因是ARC不知道,copySelector返回retain object。
如果你运行内存泄漏工具,你可以看到下面的图片: ...在其他任何情况下都没有内存泄漏:
您还可以在这里使用协议。因此,创建一个这样的协议:
@protocol MyProtocol
-(void)doSomethingWithObject:(id)object;
@end
在需要调用选择器的类中,有一个@属性。
@interface MyObject
@property (strong) id<MyProtocol> source;
@end
当你需要在MyObject的实例中调用@selector(doSomethingWithObject:)时,这样做:
[self.source doSomethingWithObject:object];
在项目生成设置中,在其他警告标志(WARNING_CFLAGS)下添加 -Wno-arc-performSelector-leaks
现在只需确保所调用的选择器不会导致对象被保留或复制。