在ARC下,out-parameter采用以下形式(默认情况下;这相当于NSError **
):
- (BOOL)tryWithError:(NSError *__autoreleasing *)err;
从Transitioning to ARC Release Notes开始,如果我们传递__strong
局部变量的地址,编译器将创建一个临时变量并生成以下代码:
NSError *error; // strong
BOOL ok = [myObject tryWithError:&error];
// translated to
NSError *__strong error;
NSError *__autoreleasing tmp = error;
BOOL ok = [myObject tryWithError:&tmp];
error = tmp;
但是,如果我们使用实例变量:
@implementation Foo {
NSError *_error; // strong
}
- (void)bar
{
[myObject tryWithError:&_error];
}
...
这给了我们错误
将非本地对象的地址传递给
__autoreleasing
参数以进行回写。
为什么这会无效?编译器是否只能将此类代码自动转换为此内容?
- (void)bar
{
NSError *__autoreleasing tmp = _error;
[myObject tryWithError:&tmp];
_error = tmp;
}
毕竟,这就是我将要编写的解决问题的方法!
注意:将out
关键字添加到参数类型will reduce the compiler's work slightly,因为它不必将当前值读入临时变量 - 但这并不需要考虑错误。子>
答案 0 :(得分:1)
指向ivar的指针无法传递给ARC下的“id __autoreleasing *”参数,因为那种pass-by-writeback格式不正确。 respective section in the ARC specification列出了pass-by-writeback的合法形式,这里唯一适用的是
& var,其中var是自动存储持续时间的标量变量 使用可保留对象
,因此只允许自动存储持续时间(局部变量)。
为什么这是无效的:我很确定这里的原因是与旧代码的兼容性:
1)You should only look at the error writeback in the failure case。在成功的情况下,根本无法保证错误指针内部的内容。
2)通常,是否应该使用回写值取决于方法的合同。这是编译器无法检查的内容。
这是将&error
(NSError * __autoreleasing *
)的类型与回写类型NSError **
(被解释为NSError * __autoreleasing *
)匹配的代码版本。如果ok
为YES,则不会触及错误值。
NSError * __autoreleasing error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
// use error
}
然而,那些__autoreleasing
是丑陋的,所以编译器允许我们传递__autoreleasing
(但是本地)变量,而不是强迫我们在整个地方使用__strong
。 (默认所有权):
NSError *error;
BOOL OK = [myObject performOperationWithError:&error];
if (!OK) {
// use error
}
根据文档,这被重写为:
NSError * __strong error;
NSError * __autoreleasing tmp = error;
BOOL OK = [myObject performOperationWithError:&tmp];
error = tmp;
if (!OK) {
// use error
}
根本不是问题,错误只会在成功案例中使用。
现在让我们看一下__strong
实例变量_error
。为什么编译器不允许这样做?这是重写的样子:
NSError * __autoreleasing tmp = _error;
BOOL OK = [myObject performOperationWithError:&tmp];
_error = tmp;
if (!OK) {
// use error
}
这里的问题是tmp
中的回写将总是被使用(分配给实例变量_error
),忽略方法的契约< / em>只应在错误情况下使用回写(或者通常无论方法的文档说什么)。将最后一个错误分配给实例变量的安全方法是
NSError * __autoreleasing tmp = _error;
BOOL OK = [myObject performOperationWithError:&tmp];
if (!OK) {
_error = tmp;
// use error
} else {
_error = nil; // Make sure that _error is nil if there was no error.
}
这对于返回错误的Cocoa方法的惯例是正确的。通常,编译器无法告诉方法对id *
的作用:可能存在使用不同约定的旧方法。因此,如果您真的想将回写存储在__strong
实例变量中,那么您当前必须自己走得更远,我不希望这会改变。