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确实无法完成在这里输入正确数量的retainrelease 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

上一篇: Action design pattern became bad practice under ARC?

下一篇: error after login into WSO2 Agent