1

例如:我们总是这样写 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 吗?

4

3 回答 3

4

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

此外,确定引用计数错误的静态分析器对于其中一些库和程序来说是相对较新的。自动释放池比 ARC 和 objc 对象引用计数的静态分析早了很多年——现在使用这些工具确保您的引用计数是正确的要简单得多。他们能够检测到许多错误。

当然,使用 ARC,如果您喜欢返回自动释放对象的简单性,那么很多情况都会发生变化——使用 ARC,您可以返回更少的自动释放对象,而不会引入抽象的自动释放池错误。

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

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

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

于 2012-09-02T04:53:49.300 回答
3

您是否衡量过性能优势?您是否有一个可量化的案例,其中autoreleaseCF 风格的 caller-must-release 具有可衡量的性能影响?

如果没有,那就没有意义了。如果是这样,我敢打赌,有一个系统架构问题远远超过自动释放与否。

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

于 2012-09-02T05:56:45.420 回答
0

主要原因有以下三个:

  1. 它强加了拥有所有权的概念。
  2. 它消除了悬空指针的问题。
  3. 它返回在本地自动释放池中创建的对象,从而提高性能。
于 2012-09-02T12:20:31.870 回答