【问题标题】:What are the advantages of declaring method arguments __autoreleasing?声明方法参数 __autoreleasing 有什么好处?
【发布时间】:2013-01-28 01:05:02
【问题描述】:

根据Transitioning to ARC Release Notes

__autoreleasing 用于表示通过引用 (id *) 传递并在返回时自动释放的参数。

例如:

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

但与上述相比有什么优势:

-(BOOL)performOperationWithError:(NSError * __strong *)error;


更新:

几个答案提到了 temp var 技巧编译器处理 var 和参数之间的不匹配作为__autoreleasing优势。我不明白为什么编译器不能为__strong 参数做同样的技巧。我的意思是,对于 __weak var 和 __strong 参数,编译器可以类似地这样做:

NSError * __weak error;
NSError * __strong tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
    // Report the error.
}

编译器知道-(BOOL)performOperationWithError:(NSError * __strong *)error; 返回一个强引用(+1),所以它就像任何new-family 方法一样处理它。由于tmperror 存在于同一范围内,因此只要error 编译器就可以合理地保持其活动状态,因此__weak 引用(error) 现在由__strong 引用(tmp) 支持) 并且在范围结束之前不会被取消。

【问题讨论】:

  • 您尝试自己管理双指针的内存,然后告诉我们为什么 __autoreleasing 如此出色。
  • @CodaFi 即使使用 __strong,ARC 也会为我们进行内存管理,不是吗?
  • 当然,但它使编译器的工作变得更加棘手。至少使用 __autoreleasing,tmp 变量被添加到内部自动释放池中(因为 NSError 的生命周期很短)。如果指针是强指针,它必须完成为第二个指针找出限定符并释放与其关联的对象的工作。无论哪种方式,你都是对的,但我宁愿使用 LLVM 的默认 __autoreleasing 双指针而不是任何 __strong 没有正当理由。

标签: objective-c automatic-ref-counting


【解决方案1】:

tl;博士

在这种情况下,将 __weak 对象隐式转换为 __strong 对象会改变程序的语义,这是编译器绝不应该做的事情。


场景

举个例子

NSError *error;
BOOL success = [myObject performOperationWithError:&error];
if (!success) {
    // Report the error
}

在这种情况下,error 局部变量被 ARC 自动推断为__strong

同时error的参数

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

类型为NSError * __autoreleasing *

请注意,在任何情况下,ARC 都会将通过引用 (id *) 传递的参数推断为 id __autoreleasing * 类型,因此上述签名等价于

-(BOOL)performOperationWithError:(NSError **)error;

在 ARC 下。

因此我们有一个不匹配,因为我们将 __strong 带注释的变量传递给期望 __autoreleasing 参数的方法。

引擎盖下

在我们的示例中,编译器将通过创建本地 __autoreleasing tmp 变量来解决这种不匹配问题。

代码变成

NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL success = [myObject performOperationWithError:&tmp];
error = tmp;
if (!success) {
    // Report the error.
}

另一种选择

现在假设我们可以更改performOperationWithError: 的签名。

如果我们想避免使用tmp 变量的“编译器技巧”,我们可以将我们的签名声明为

-(BOOL)performOperationWithError:(NSError * __strong *)error;

我们有一个__strong 变量,我们现在将它传递给一个需要__strong 参数的方法,所以我们只是消除了不匹配。

看起来不错,为什么不总是声明 __strong 参数?

一个原因是,将参数声明为 __autoreleasing 将使该方法甚至接受 __weak 引用。

在当前示例中没有多大意义,但在某些情况下,我们希望通过引用传递 __weak 变量并声明 __autoreleasing(或让 ARC 推断它)将允许我们这样做。

ARC 将应用上面看到的相同技巧,创建一个 __autoreleasing tmp 变量。

结论

目前介绍的机制名为pass-by-writeback

这种机制被设计为与__autoreleasing__strong__weak 变量一起使用,因此程序员可以安全地依赖编译器所做的类型推断,而不必过多关心周围的变量注释。

声明id __strong * 参数在某些情况下可能有意义,但通常会导致编译器生成意外错误。

我的建议是:“让编译器发挥他的魔力,你会好起来的”

更新

我不明白为什么编译器不能为 __strong 参数做同样的伎俩。

告诉编译器以__autoreleasing 方式处理__strong__weak 变量的管理没关系,因为它基本上意味着:“请编译器,自动做正确的事情”。

这就是为什么上面看到的技巧可以正常工作的原因。

另一方面,如果您将变量声明为__weak,您可能有充分的理由这样做,并且您最不想做的就是在您明确指定时隐式保留它。这会从根本上改变您编写的代码的语义,因此编译器不会这样做(感谢上帝!)。

换句话说

__weak --> __autoreleasing
__strong --> __autoreleasing
__weak __strong错了!

【讨论】:

  • 我的问题不是“为什么不总是声明 __strong 参数?”,而是“为什么默认声明__autoreleasing 参数?” ARC 也可以应用tmp var 技巧来处理__strong__weak 之间的不匹配,不是吗?
  • 如果您仔细阅读,您会发现您的问题得到了解答。默认情况下声明__autoreleasing 只会提供更大的灵活性。当然你可以这样做,但是让编译器为你这样做肯定更方便、更安全。
  • @GabrielePetronella 那么问题是为什么__autoreleasing__strong 更灵活?我不明白为什么编译器不能为__strong 参数做同样的临时变量技巧。如果您所指的优势仅基于现在编译器只能对__autoreleasing 执行临时变量技巧,那么在我看来,这不是__autoreleasing 的真正优势,而是编译器的缺点。
  • @GabrielePetronella 请在问题中查看我的更新。谢谢。
  • @GabrielePetronella 我同意如果编译器为 __weak var 插入 temp __strong var,语义会发生变化。这部分切入我的问题。更好的问题之后才有更好的答案:) 谢谢!
【解决方案2】:

正如您所说,唯一的优点是对象在返回时自动释放。因此,如果没有 ARC,它与发送保留和自动释放是一样的。在 C 中,作为参数传递的每个变量都会被复制,因此这不会影响原始指针的处理方式,而只会影响复制的指针。

优势的一个例子可能是这样,假设参数不是 __autoreleasing:

-(BOOL)performOperationWithError:(NSError * __strong *)error;

所以我调用传递弱引用的方法:

NSError* __weak error;
[object performSelectorWithError: &error];

这里发生了什么?复制的参数在返回时不会自动释放,因此当方法返回时错误为零。如果相反,方法是这个:

-(BOOL)performOperationWithError:(NSError * __autoreleasing *)error;

在这种情况下,错误的保留计数仍然为 1,但它是自动释放的,因此它不是 nil 并且可以在池中使用。

【讨论】:

    【解决方案3】:

    我还没有看到提到的另一个原因是遵循 Cocoa 约定,以便 ARC 代码可以与非 ARC 代码正确互操作。

    【讨论】:

      猜你喜欢
      • 2012-06-29
      • 1970-01-01
      • 2018-10-26
      • 2011-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-05
      • 2014-10-09
      相关资源
      最近更新 更多