7

根据过渡到 ARC 发行说明

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

例如:

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

但与以下相比,上述优势有哪些:

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


更新:

有几个答案是指编译器使用temp var 技巧来处理 var 和参数之间的不匹配作为__autoreleasing. 我不明白为什么编译器不能对__strong参数做同样的把戏。我的意思是,对于__weakvar 和__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 方法一样处理它。由于tmp与 位于同一范围内,因此error编译器可以合理地保持其活动,只要reference( ) 现在由reference( ) 支持,并且在范围结束之前不会被取消。error__weakerror__strongtmp

4

3 回答 3

8

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;

弧下。

因此我们有一个不匹配,因为我们将一个带__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参数?

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

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

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

结论

到目前为止提出的机制名为pass-by-writeback

这种机制被设计为与__autoreleasing,__strong__weak变量一起使用,因此程序员可以安全地依赖编译器所做的类型推断,而不必太在意变量的注释。

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

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

更新

我不明白为什么编译器不能对__strong参数做同样的把戏。

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

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

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

换句话说

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

于 2013-01-28T02:12:16.917 回答
2

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

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

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

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

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

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

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

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

于 2013-01-28T12:52:12.543 回答
1

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

于 2013-10-05T21:23:41.010 回答