多年来,我一直遵循一个很好的模式,叫做“目标-行动”,它是这样的:
对象在调用时调用指定目标对象上的指定选择器。对于需要对任意方法进行简单回调的许多不同情况,这是非常有用的。
下面是一个例子:
- (void)itemLoaded {
[specifiedReceiver performSelector:specifiedSelector];
}在ARC下,这样的事情突然变得危险起来。
Xcode抛出如下警告:
PerformSelector可能会导致泄漏,因为它的选择器未知。
当然,选择器是未知的,因为作为Target-Action设计模式的一部分,您可以指定任何您想要的选择器,以便在发生有趣的事情时得到调用。
这个警告最让我烦恼的是,它说可能有一个潜在的内存泄漏。据我的理解,ARC并不会扭曲内存管理规则,而只是在正确的位置自动插入retain/retain/ automates消息。
这里要注意的另一件事是:-performSelector:确实有一个id返回值。通过应用命名约定分析方法签名,确定方法是否返回+1保留计数对象。在这种情况下,ARC不知道选择器是一个-newFooBar工厂,还是简单地调用一个不可疑的worker方法(在任何情况下都是这样)。实际上ARC应该已经认识到我并不期望返回值,因此忘记任何潜在的+1保留计数的返回值。从这个角度来看,我可以看到ARC是从哪里来的,但在实践中,这意味着什么仍然有太多的不确定性。
这是否意味着在ARC下,如果没有ARC,就有可能出问题?我看不出这怎么会产生内存泄漏。有人能给出这样做很危险的情况的例子吗?在这种情况下,泄漏是如何产生的?
我真的在网上搜索过,但没有找到任何解释原因的网站。
发布于 2012-01-13 18:59:06
performSelector的问题是,ARC不知道选择器将执行什么操作。请考虑以下几点:
id anotherObject1 = [someObject performSelector:@selector(copy)];
id anotherObject2 = [someObject performSelector:@selector(giveMeAnotherNonRetainedObject)];现在,ARC如何知道第一个返回保留计数为1的对象,而第二个返回自动释放的对象?(我只是在这里定义一个名为giveMeAnotherNonRetainedObject的方法,它返回一些自动发布的内容)。如果它没有添加任何版本,那么anotherObject1就会在这里泄漏。
显然,在我的示例中,要执行的选择器实际上是已知的,但假设它们是在运行时选择的。因为它根本不知道选择器要做什么,所以它不能在这里设置正确数量的retains或releases。你说得对,ARC并没有扭曲任何规则,它只是为您添加了正确的内存管理调用,但这正是它在这里所不能做的。
你是对的,你忽略了返回值,这意味着它将是好的,但一般来说,ARC只是挑剔和警告。但我想这就是为什么这是一个警告而不是一个错误。
编辑:
如果您确实确定您的代码没有问题,您可以将警告隐藏如下:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[specifiedReceiver performSelector:specifiedSelector];
#pragma clang diagnostic pop发布于 2012-01-13 19:15:05
警告应该是这样的:
PerformSelector可能会导致泄漏,因为它的选择器未知。弧线不知道返回的id是否有+1保留计数,因此无法正确管理返回对象的内存。
不幸的是,这只是第一句。
现在解决办法是:
如果从-performSelector方法接收返回值,则除了忽略它之外,不能对代码中的警告进行任何处理。
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];你最好的选择是:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];
#pragma clang diagnostic pop在我最初的问题中,情况也是如此,我完全忽略了返回值。弧形应该足够聪明,可以看到我不关心返回的id,因此匿名选择器几乎可以保证不会是工厂、方便构造函数或其他任何东西。不幸的是ARC不是,所以同样的规则也适用。无视警告。
它也可以通过在项目构建设置中的“其他警告标志”下设置-Wno-弧形-性能选择器-泄漏编译器标志来完成整个项目。
或者,当您在目标>“构建阶段”>“编译源”下添加标记时,可以在每个文件的基础上压制警告。
这三个解决方案都是非常混乱的IMHO,所以我希望有人想出一个更好的。
发布于 2012-10-02 08:41:27
如前所述,您会得到这样的警告,因为编译器不知道将performSelector:返回值的保留/发布放在何处(或是否)。
但是请注意,如果使用[someObject performSelector:@selector(selectorName)],它将不会生成警告(至少在Xcode 4.5和llvm4.1中是这样),因为确切的选择器很容易确定(您显式地设置了它),这就是为什么编译器能够将retain/releases放在正确的位置。
这就是为什么只有在使用SEL指针传递选择器时才会收到警告,因为在这种情况下,编译器无法确定要做什么。因此,使用以下方法
SEL s = nil;
if(condition1) SEL = @selector(sel1)
else SEL = @selector(sel2)
[self performSelector:s];会产生警告。但将其重构为:
if(condition1) [self performSelector:@selector(sel1)]
else [self performSelector:@selector(sel2)]不会产生任何警告。
https://stackoverflow.com/questions/8855461
复制相似问题