NSError和__autoreleasing

42

请问有人可以解释一下以下示例代码块中使用__autoreleasing的目的吗?

- (void)execute:(NSError * __autoreleasing *)error {
    // do stuff, possibly assigning error if something went wrong
}

我删除了__autoreleasing并且一切似乎都能够编译/运行,我是在ARC之后开始使用obj-c的,因此从未真正学习/理解过这些双下划线的东西。我已经阅读了ARC转换指南,但是对于其中的NSError示例并没有完全理解。

1个回答

87

考虑 ARC 如何与变量配合工作——每个引用变量都具有模式(隐式或显式):强引用弱引用等。这种模式可以让 ARC 知道如何处理对该变量的读写操作;例如,对于强引用变量,读取不需要额外的操作,而写入需要在替换为新值之前释放变量中现有的引用。ARC 需要知道任何变量的模式才能发挥作用。

现在考虑那些通过引用本身传递的变量,例如,对于您的execute,您将进行以下调用:

NSError *myError = nil;
...
[someObject execute:&myError]; // pass the variable itself by reference, not the variables value

execute 的主体将包含类似以下的赋值语句:

- (void)execute:(NSError * __autoreleasing *)error
{
   ...
   if (error != NULL)
      *error = [NSError ...]; // assign indirectly via the reference to a variable
   ...
}

现在对于那个间接赋值,ARC需要知道引用变量的模式以便于读写。这就是声明中的__autoreleasing所起的作用,它告诉ARC传递了一个引用模式为autoreleasing的变量,并且这告诉ARC如何读写变量的内容。去掉__autoreleasing会默认假设一个模式,在这种情况下,我建议明确指定。

autoreleasing模式意味着变量包含一个未拥有的引用。如果需要,读操作应该retain,而写操作可以直接写入。主要用于通过引用传递的变量。

你可能注意到在上面的示例中,变量myError的模式为strong(隐式),但它作为autoreleasing的引用传递 - 编译器通过引入一个临时的autoreleasing变量来自动处理这个问题,将myError中当前引用的副本复制到其中,然后将临时变量作为execute:的参数按引用传递。调用返回后,编译器从临时变量进行正常的赋值到myError,这会释放任何旧引用并保留返回的引用。

有关更多详细信息,请参见苹果公司的自动引用计数(ARC)转换发布说明

回应评论

问:是否会隐式设置__autoreleasing?

答:嗯,苹果公司的文件没有具体说明,但是Clang文档说对于间接参数是隐含的。如上所述,我建议明确指定,清晰度是件好事™。

问:位置是否重要?

答:是和不是...这是一个C声明,经常作为问答题出现(“下面的声明是什么意思…”)。限定符应该位于两个星号之间,因为它是指向对象的autoreleasing指针类型的变量,但是苹果公司表示编译器是“宽容”的,而没有明确说明它原谅了什么。保险起见,把它放在正确的位置。

问:在进行间接赋值之前,是否应该测试error是否为NULL

答:当然,应该在间接引用之前的某个地方进行测试。所示代码只是一个概述,并且这样的细节被省略了并由...覆盖。但是由于多年来已经提出了几次,也许我省略了太多的细节,因此已添加了适当的if


1
如果我没有显式设置,那么__autoreleasing是否会被隐式设置?另外,__autoreleasing的放置位置是否重要(在星号之前、内部还是之后)? - chinabuffet
@chinabuffet - 请查看回答中的后续内容。 - CRD
2
@CRD 很好的回答。但是你可能需要指出,在给 *error 赋值之前,应该检查 error 是否为 NULL。否则,由于 [foo execute:NULL] 是一个完全有效的执行方式,表示“我不关心错误”,你的方法可能会导致应用程序崩溃。 - Joel
2
在XCode 6.1中,如果你没有明确指定,会出现警告:“方法参数的类型为 'NSError *__autoreleasing *',没有明确的所有权”。 - Mickaël Rémond
同意@Joel的观点,您需要在分配*error之前检查error是否不是nilNULL,否则可能会引发EXC_BAD_ACCESS信号。 - pxpgraphics

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接