我正在尝试完成这个谜题。
__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 *);
但这太模糊了,我想完全理解为什么。我发现的代码片段将 __autoreleasing 放在两颗星之间,这对我来说看起来很奇怪。类型是 NSError**
(指向 NSError 的指针),那么为什么要将 __autoreleasing
放在星星之间而不是简单地放在 NSError**
前面呢?
此外,在其他情况下,我可能必须依赖 __autoreleasing
。
你是对的。正如官方文档解释的那样:
__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
来避免这种情况。
在评论中跟进 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 对象指针的指针”。
definitive ARC specification 表示
对于 __autoreleasing 对象,新指针被保留、自动释放并使用原始语义存储到左值中。
例如,代码
NSError* __autoreleasing error = someError;
实际上被转换为
NSError* error = [[someError retain] autorelease];
...这就是为什么当您有参数 NSError* __autoreleasing * errorPointer
时它起作用的原因,然后被调用的方法会将错误分配给 *errorPointer
并且上述语义将起作用。
您可以在不同的上下文中使用 __autoreleasing
来强制将 ARC 对象放入自动释放池,但这并不是非常有用,因为 ARC 似乎只在方法返回时使用自动释放池并且已经自动处理了。
简而言之:这只是为了与 MRC 兼容。
Apple 已同意在自己的库中 **
返回的对象始终是自动释放的。因此,ARC
代码适用于旧的二进制文件(例如,如果您的部署目标是 iOS 4),反之亦然,MRC
代码适用于 ARC
二进制文件。
所以总而言之:
你永远不应该使用 __autoreleasing:编译器会在需要的地方自动添加它
如果你不打算支持 MRC 代码,那么你应该在任何地方使用 * __strong *。它将从家庭崩溃中保存:@autoreleasingpool { *autorelesingOut = [@"crash maker" mutableCopy];//NSString * __autoreleasing *autorelesingOut; *strongOut = [@"没关系" mutableCopy];//NSString * __strong *strongOut; //如果 autorelesingOut 将在此 autoreleasepool 之外被引用,应用程序将崩溃 }
__autoreleasing
仅用于通过引用传递的参数。这是一种特殊情况,因为您有一个指向对象指针的指针。便利构造函数之类的东西并非如此,因为它们只返回一个指向对象的指针,并且 ARC 会自动处理它。