首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >目标-行动设计模式是否成为ARC下的不良实践?

目标-行动设计模式是否成为ARC下的不良实践?
EN

Stack Overflow用户
提问于 2012-01-13 18:35:24
回答 4查看 2.9K关注 0票数 35

多年来,我一直遵循一个很好的模式,叫做“目标-行动”,它是这样的:

对象在调用时调用指定目标对象上的指定选择器。对于需要对任意方法进行简单回调的许多不同情况,这是非常有用的。

下面是一个例子:

代码语言:javascript
复制
- (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,就有可能出问题?我看不出这怎么会产生内存泄漏。有人能给出这样做很危险的情况的例子吗?在这种情况下,泄漏是如何产生的?

我真的在网上搜索过,但没有找到任何解释原因的网站。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-01-13 18:59:06

performSelector的问题是,ARC不知道选择器将执行什么操作。请考虑以下几点:

代码语言:javascript
复制
id anotherObject1 = [someObject performSelector:@selector(copy)];
id anotherObject2 = [someObject performSelector:@selector(giveMeAnotherNonRetainedObject)];

现在,ARC如何知道第一个返回保留计数为1的对象,而第二个返回自动释放的对象?(我只是在这里定义一个名为giveMeAnotherNonRetainedObject的方法,它返回一些自动发布的内容)。如果它没有添加任何版本,那么anotherObject1就会在这里泄漏。

显然,在我的示例中,要执行的选择器实际上是已知的,但假设它们是在运行时选择的。因为它根本不知道选择器要做什么,所以它不能在这里设置正确数量的retains或releases。你说得对,ARC并没有扭曲任何规则,它只是为您添加了正确的内存管理调用,但这正是它在这里所不能做的。

你是对的,你忽略了返回值,这意味着它将是好的,但一般来说,ARC只是挑剔和警告。但我想这就是为什么这是一个警告而不是一个错误。

编辑:

如果您确实确定您的代码没有问题,您可以将警告隐藏如下:

代码语言:javascript
复制
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[specifiedReceiver performSelector:specifiedSelector];
#pragma clang diagnostic pop
票数 25
EN

Stack Overflow用户

发布于 2012-01-13 19:15:05

警告应该是这样的:

PerformSelector可能会导致泄漏,因为它的选择器未知。弧线不知道返回的id是否有+1保留计数,因此无法正确管理返回对象的内存。

不幸的是,这只是第一句。

现在解决办法是:

如果从-performSelector方法接收返回值,则除了忽略它之外,不能对代码中的警告进行任何处理。

代码语言:javascript
复制
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];

你最好的选择是:

代码语言:javascript
复制
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSArray *linkedNodes = [startNode performSelector:nodesArrayAccessor];
#pragma clang diagnostic pop

在我最初的问题中,情况也是如此,我完全忽略了返回值。弧形应该足够聪明,可以看到我不关心返回的id,因此匿名选择器几乎可以保证不会是工厂、方便构造函数或其他任何东西。不幸的是ARC不是,所以同样的规则也适用。无视警告。

它也可以通过在项目构建设置中的“其他警告标志”下设置-Wno-弧形-性能选择器-泄漏编译器标志来完成整个项目。

或者,当您在目标>“构建阶段”>“编译源”下添加标记时,可以在每个文件的基础上压制警告。

这三个解决方案都是非常混乱的IMHO,所以我希望有人想出一个更好的。

票数 9
EN

Stack Overflow用户

发布于 2012-10-02 08:41:27

如前所述,您会得到这样的警告,因为编译器不知道将performSelector:返回值的保留/发布放在何处(或是否)。

但是请注意,如果使用[someObject performSelector:@selector(selectorName)],它将不会生成警告(至少在Xcode 4.5和llvm4.1中是这样),因为确切的选择器很容易确定(您显式地设置了它),这就是为什么编译器能够将retain/releases放在正确的位置。

这就是为什么只有在使用SEL指针传递选择器时才会收到警告,因为在这种情况下,编译器无法确定要做什么。因此,使用以下方法

代码语言:javascript
复制
SEL s = nil;
if(condition1) SEL = @selector(sel1)
else SEL = @selector(sel2)

[self performSelector:s];

会产生警告。但将其重构为:

代码语言:javascript
复制
if(condition1) [self performSelector:@selector(sel1)]
else [self performSelector:@selector(sel2)]

不会产生任何警告。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8855461

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档