为什么callee返回自动释放的对象而不是返回保留的对象并让调用者释放对象?

时间:2012-09-02 04:22:09

标签: iphone objective-c ios cocoa

例如: 我们总是这样写1:

-(NSObject*)giveMeAnObject
{
    return [[[NSObject alloc] init] autorelease];
}

-(void)useObject
{
    NSObject *object = [self giveMeAnObject];
    //use that object
}

但为什么我们不这样写2:

-(NSObject*)giveMeAnObject
{
    return [[NSObject alloc] init];
}

-(void)useObject
{
    NSObject *object = [self giveMeAnObject];
    //use that object
    [object release];
}

Cocoa SDK做的方式类似于1,我认为这就是为什么我们都使用方式1,它已经成为一种编码约定。 但我只是认为,如果惯例是第2条,我们可以获得很少的性能提升。

除了编码惯例之外,还有其他原因我们使用方式1而不是方式2吗?

3 个答案:

答案 0 :(得分:4)

返回一个自动释放的对象是一种抽象形式 - 对开发人员来说很方便,因此他/她不必考虑返回对象的引用计数 - 从而导致特定类别中的错误减少(尽管您也可以说自动释放池会引入类别的错误或复杂性)。它可以真正简化客户端代码,尽管是的,可能会有性能损失。当不必进行引用操作时,它也可以用作抽象优化 - 考虑对象何时保存返回的实例并且不需要保留或复制。尽管链接语句可能被过度使用,但这种做法对于链接语句也很方便。

此外,确定引用计数错误的静态分析器对于某些库和程序来说相对较新。自动释放池在ARC之前和objc对象的引用计数的静态分析多年 - 确保您的引用计数正确现在使用这些工具更加简单。他们能够发现许多错误。

当然,对于ARC,如果你喜欢返回自动释放对象的简单性,那么很多都会改变 - 使用ARC,你可以返回更少的自动释放对象而不会引入抽象的自动释放错误池。

使用统一所有权语义还可以简化程序。如果抽象选择器或选择器集合总是使用相同的语义返回,那么它可以真正简化一些通用形式。例如 - 如果传递给performSelector:的一组选择器具有不同的返回所有权语义,那么它会增加很多复杂性。因此,返回时的统一所有权可以真正简化一些更“通用”的实现。

性能:引用计数操作(保留/释放)可能相当重要 - 特别是如果您习惯于在较低级别工作。但是,当前的自动释放池实现速度非常快。它们最近更新了,并且比以前更快。编译器和运行时使用几个特殊的快捷方式来降低这些成本。保持自动释放池大小也是一个好主意 - 特别是在移动设备上。创建自动释放池非常快。在实践中,您可能会看到自动释放操作本身从零增加到几个百分点(即它比objc_msgSend +变体消耗的时间少得多)。有些测试甚至跑得快一些。这不是很多人会从中获益的优化。在典型的情况下,它不符合低悬的水果,实际上相对难以在实际程序中测量这些变化的影响和位置 - 基于我在bbum提到下面的变化后做的一些测试。因此测试的范围有限,但在MRC和ARC中似乎更好/更快。

因此,如果您正在执行自己的引用计数操作,那么很多都归结为您要承担的责任级别。对于大多数人来说,它不应该真正改变他们的写作方式。我认为本地化内存问题,更多确定性对象破坏以及更可预测的堆大小是主要原因,如果您在现代系统上运行,可能有利于返回“拥有”(+1)引用计数。即便如此,在许多情况下,运行时和编译器都会为您减少这一点(请参阅bbum的答案+1)。虽然自动释放池的速度差不多,但我现在不打算比现在更多地使用它们 - 所以仍有合理的理由尽量减少使用它们,但原因正在减少。

答案 1 :(得分:3)

您是否测量过性能优势?您是否有一个可量化的案例,autorelease与CF风格的调用者必须发布具有可衡量的性能影响?

如果没有,没有实际意义。如果是这样,我敢打赌,存在一个系统架构问题远远超过自动释放而不是。

无论如何,如果采用ARC,自动释放的“成本”会降至最低。编译器和运行时实际上可以跨方法调用优化自动释放。

答案 2 :(得分:0)

主要有三个原因:

  1. 它强加了取得所有权的概念。
  2. 它消除了悬空指针的问题。
  3. 它返回在本地自动释放池中创建的对象,从而提高性能。