当我们使用performSelector方法的时候,如:
[object performSelector:@selector(someMethod)];
[object performSelector:NSSelectorFromString(@"someMethod")];
在ARC情况下,可能会导致编译器报如下警告:
"performSelector may cause a leak because its selector is unknown".
原因
由于这段代码是动态的调用someMethod方法,编译器并不了解这个方法的返回值,因此编译器也无法用ARC的内存管理机制来进行管理。ARC对此的做法就是不添加释放操作,因此当调用的方法会使调用对象引用计数增加时,将会导致内存泄露。
解决方法
1.利用C函数指针方法调用函数
你可以将上面的方法调用改写成如下方式:
if (!object) { return; }
SEL selector = NSSelectorFromString(@"someMethod");
IMP imp = [object methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(object, selector);
或者,你可以写的更简单一点:
SEL selector = NSSelectorFromString(@"someMethod");
((void (*)(id, SEL))[object methodForSelector:selector])(object, selector);
如果方法有返回值或者需要传入参数的话,你需要做一些改变,例如:
SEL selector = NSSelectorFromString(@"processRegion:ofView:");
IMP imp = [object methodForSelector:selector];
CGRect (*func)(id, SEL, CGRect, UIView *) = (void *)imp;
CGRect result = object ? func(object, selector, someRect, someView) : CGRectZero;
2.让编译器忽略这些警告
通过使用clang diagnostic ignored来使编译器忽略指定的警告,如果你有多处报这个警告的话,你可以定义如下的宏来使代码更简洁。
#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]
);
总结
当你无法确定调用的方法是否会造成内存泄露时,最好不要这样做。例如当你在调用类似initSomething、newSomething、somethingCopy等会使引用计数增加方法时,是可能会导致内存泄露。例如,如下的调用回导致内存泄漏:
NSArray *array = @[@"a",@"b"];
[array performSelector:NSSelectorFromString(@"copy")];
在Xcode7.3版本中,使用如下写法:
NSArray *array = @[@"a",@"b"];
[array performSelector:@selector(copy)];
此时编译器会直接报错:
PerformSelector names a selector which retains the object
这时无论使用上述哪种方法都无法解决内存泄漏。在MRC模式下,加上array = nil;是可以解决内存泄漏的,但是在ARC模式下不行。在网上也看了好多资料,试了多种方法,但这个问题还是没有解决。所以,在ARC模式下,尽量不要使用performSelector方法来调用initSomething、newSomething、somethingCopy等会使引用计数增加方法。