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
错了!