ARC下的行为设计模式变得糟糕吗?
多年来,我一直在关注一个名为Target-Action的伟大模式,如下所示:
当时间到来时,对象在指定的目标对象上调用指定的选择器。 这在许多不同的情况下非常有用,您需要对任意方法进行简单回调。
这是一个例子:
- (void)itemLoaded {
[specifiedReceiver performSelector:specifiedSelector];
}
在ARC之下,现在证明,突然间做这样的事情变得危险了。
Xcode抛出一个警告,如下所示:
由于其选择器未知,PerformSelector可能会导致泄漏
当然,选择器是未知的,因为作为Target-Action设计模式的一部分,您可以指定任何您想要的选择器,以便在发生有趣的事情时接听电话。
这个警告最让我感到困惑的是它说可能会有潜在的内存泄漏。 根据我的理解,ARC不会弯曲内存管理规则,而只是在正确的位置自动插入保留/释放/自动释放消息。
另外需要注意的是:-performSelector:确实有一个id
返回值。 如果方法返回+1保留计数对象,则ARC通过应用命名约定分析方法签名。 在这种情况下,ARC不知道选择器是否是-newFooBar
工厂,或者只是调用一个不可靠的工作者方法(反正Target-Action几乎总是这样)。 实际上,ARC应该已经认识到我不希望有返回值,因此忘记了任何潜在的+1保留计数的返回值。 从这个角度来看,我可以看到ARC是从哪里来的,但仍然存在着太多的不确定性,这在实践中意味着什么。
那现在是否意味着在ARC之下会出现什么问题,这在没有ARC的情况下不会发生? 我不明白这会如何产生内存泄漏。 有人可以举出这种做法很危险的情况,以及在这种情况下如何产生泄漏?
我真的从互联网上下了地狱,但没有找到任何网站解释为什么。
performSelector
的问题在于ARC不知道将执行的选择器是什么。 考虑以下:
id anotherObject1 = [someObject performSelector:@selector(copy)];
id anotherObject2 = [someObject performSelector:@selector(giveMeAnotherNonRetainedObject)];
现在,ARC如何知道第一个返回保留计数为1的对象,但第二个返回的是一个自动释放的对象? (我只是在这里定义一个名为giveMeAnotherNonRetainedObject
的方法,它返回一些自动发布的东西)。 如果它没有添加任何版本,那么anotherObject1
会在这里泄漏。
显然,在我的例子中,要执行的选择器实际上是已知的,但想象它们是在运行时选择的。 ARC确实无法完成在这里输入正确数量的retain
或release
s的工作,因为它根本不知道选择器将要做什么。 你说得对,ARC没有弯曲任何规则,它只是为你加入正确的内存管理调用,但这正是它在这里无法做到的事情。
你是对的,你忽略了回报价值的事实意味着它会好起来的,但一般来说ARC只是挑剔和警告。 但我想这就是为什么这是一个警告,而不是一个错误。
编辑:
如果你确定你的代码是可以的,你可以像这样隐藏警告:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[specifiedReceiver performSelector:specifiedSelector];
#pragma clang diagnostic pop
警告应该像这样读取:
由于其选择器未知,PerformSelector可能会导致泄漏。 ARC不知道返回的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
在我最初的问题中,我完全忽略了返回值。 ARC应该足够聪明,看看我不关心返回的id,因此匿名选择器几乎可以保证不是工厂,便捷构造器或任何其他的东西。 不幸的是ARC不是,所以同样的规则适用。 忽略警告。
也可以通过在项目构建设置中的“其他警告标志”下设置-Wno-arc-performSelector-leaks编译器标志来完成整个项目。
或者,您可以在每个文件的基础上抑制警告,当您在目标>“生成阶段”>“编译源”下的所需文件旁边右侧添加该标志时。
所有这三个解决方案都非常混乱,所以我希望有人提出更好的解决方案。
如上所述,您会收到该警告,因为编译器不知道在哪里(或如果)放置performSelector的保留/释放:返回值。
但是请注意,如果使用[someObject performSelector:@selector(selectorName)]
,它将不会生成警告(至少在Xcode 4.5中使用llvm 4.1),因为确切的选择器很容易确定(您明确地设置它),这就是为什么编译器能够将保留/释放放在正确的位置。
这就是为什么只有在使用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)]
不会产生任何警告
链接地址: http://www.djcxy.com/p/19945.html