在哪些情况下我们需要在 ARC 下编写 __autoreleasing 所有权限定符?
Posted
技术标签:
【中文标题】在哪些情况下我们需要在 ARC 下编写 __autoreleasing 所有权限定符?【英文标题】:In which situations do we need to write the __autoreleasing ownership qualifier under ARC? 【发布时间】:2012-02-10 07:58:45 【问题描述】:我正在尝试完成这个谜题。
__strong
是所有 Objective-C 可保留对象指针(如 NSObject、NSString 等)的默认值。它是一个强引用。 ARC 在作用域末尾用-release
平衡它。
__unsafe_unretained
等于旧方式。用于不保留可保留对象的弱指针。
__weak
与__unsafe_unretained
类似,只是它是一个自动归零的弱引用,这意味着一旦引用的对象被释放,指针就会被设置为 nil。这消除了悬空指针和 EXC_BAD_ACCESS 错误的危险。
但是__autoreleasing
到底有什么用呢?我很难找到关于何时需要使用此限定符的实际示例。我相信它仅适用于需要指针指针的函数和方法,例如:
- (BOOL)save:(NSError**);
或
NSError *error = nil;
[database save:&error];
在 ARC 下必须这样声明:
- (BOOL)save:(NSError* __autoreleasing *);
但这太模糊了,我想完全理解为什么。我发现的代码 sn-ps 将 __autoreleasing 放在两颗星之间,这对我来说看起来很奇怪。类型是NSError**
(指向NSError 的指针),那么为什么要将__autoreleasing
放在星星之间而不是简单地放在NSError**
前面呢?
另外,在其他情况下我可能必须依赖__autoreleasing
。
【问题讨论】:
我有同样的问题,下面的答案并不完全令人信服......例如,为什么系统不提供接口来接受像你这样的 __autoreleasing 装饰器声明的 NSError** 参数和过渡到 Arc 发行说明说他们应该是?例如,NSFileManager.h 中的许多这些例程中的任何一个?? 【参考方案1】:你是对的。正如官方文档解释的那样:
__autoreleasing 表示通过引用 (id *) 传递并在返回时自动释放的参数。
所有这些都在ARC transition guide 中得到了很好的解释。
在您的 NSError 示例中,声明意味着 __strong
,隐含:
NSError * e = nil;
将转化为:
NSError * __strong error = nil;
当您调用 save
方法时:
- ( BOOL )save: ( NSError * __autoreleasing * );
然后编译器必须创建一个临时变量,设置为__autoreleasing
。所以:
NSError * error = nil;
[ database save: &error ];
将转化为:
NSError * __strong error = nil;
NSError * __autoreleasing tmpError = error;
[ database save: &tmpError ];
error = tmpError;
您可以通过直接将错误对象声明为__autoreleasing
来避免这种情况。
【讨论】:
不,__autoreleasing
仅用于通过引用传递的参数。这是一种特殊情况,因为您有一个指向对象指针的指针。便利构造函数之类的东西并非如此,因为它们只返回一个指向对象的指针,而 ARC 会自动处理它。
为什么 __autoreleasing 限定符放在星星之间,而不是放在 NSError** 前面?这对我来说看起来很奇怪,因为类型是 NSError**。还是因为这试图表明指向的 NSError* 指针必须被限定为指向自动释放的对象?
@Proud Member 关于您的第一条评论 - 这是不正确的(如果我理解正确的话) - 请参阅下面的 Glen Low 的回答。错误对象被创建并分配给一个自动释放变量(你传入的那个)inside save 函数。此分配导致对象在当时被保留和自动释放。 save 函数的声明阻止我们向它发送除自动释放变量以外的任何内容,因为这是它所需要的 - 这就是为什么如果我们尝试编译器会创建一个临时变量。
那么为什么苹果的界面似乎都没有这个?例如,NSFileManager.h 中的所有内容?
@Macmade:碰巧我注意到您的答案已被编辑(***.com/users/12652/comptrol),我的印象是至少对您的第一个示例进行了更改(“隐式......转换为 ...) 是错误的,因为 __strong 限定符已从第二行移至第一行。也许您可以检查一下。【参考方案2】:
在 cmets 中跟进 Macmade 的回答和 Proud Member 的后续问题,(也会将此作为评论发布,但它超过了最大字符数):
这就是为什么 __autoreleasing 的变量限定符放在两颗星之间的原因。
首先,用限定符声明对象指针的正确语法是:
NSError * __qualifier someError;
编译器会原谅这个:
__qualifier NSError *someError;
但这是不正确的。请参阅the Apple ARC transition guide(阅读以“您应该正确地装饰变量...”开头的部分)。
解决手头的问题:双指针不能有 ARC 内存管理限定符,因为指向内存地址的指针是指向原始类型的指针,而不是指向对象的指针。但是,当您声明双指针时,ARC 确实想知道第二个指针的内存管理规则是什么。这就是为什么双指针变量被指定为:
SomeClass * __qualifier *someVariable;
所以在方法参数是双 NSError 指针的情况下,数据类型声明为:
- (BOOL)save:(NSError* __autoreleasing *)errorPointer;
在英文中是“指向 __autoreleasing NSError 对象指针的指针”。
【讨论】:
谢谢。这就是我需要的答案,您可以设置“因为指向内存地址的指针是指向原始类型的指针,而不是指向对象的指针”。用粗体表示,这是事情的核心。【参考方案3】:definitive ARC specification 这么说
对于 __autoreleasing 对象,新指针被保留、自动释放,并使用原始语义存储到左值中。
例如,代码
NSError* __autoreleasing error = someError;
实际上被转换为
NSError* error = [[someError retain] autorelease];
...这就是为什么当你有一个参数NSError* __autoreleasing * errorPointer
时它可以工作的原因,然后被调用的方法会将错误分配给*errorPointer
并且上面的语义将起作用。
您可以在不同的上下文中使用__autoreleasing
来强制将 ARC 对象放入自动释放池,但这并不是非常有用,因为 ARC 似乎只在方法返回时使用自动释放池并且已经自动处理了。
【讨论】:
【参考方案4】:简而言之:这只是为了与 MRC 兼容。
Apple 已同意在自己的库中**
返回的对象始终是自动释放的。所以ARC
代码可以与旧的二进制文件一起正常工作(例如,如果您的部署目标是 ios 4),反之亦然,MRC
代码也可以与ARC
二进制文件一起正常工作。
总之:
你不应该使用__autoreleasing
:编译器会在需要的地方自动添加它
如果你不打算支持 MRC 代码,那么你应该在任何地方使用* __strong *
。它将从家庭崩溃中拯救:
@autoreleasingpool
*autorelesingOut = [@"crash maker" mutableCopy];//NSString * __autoreleasing *autorelesingOut;
*strongOut = [@"it's ok" mutableCopy];//NSString * __strong *strongOut;
//App will crash if autorelesingOut will be referenced outside of this autoreleasepool
【讨论】:
以上是关于在哪些情况下我们需要在 ARC 下编写 __autoreleasing 所有权限定符?的主要内容,如果未能解决你的问题,请参考以下文章