我正在拼命尝试了解__autoreleasing
中Objective-C
关键字的用法。我已经彻底阅读了以下问题的答案:
In which situations do we need to write the __autoreleasing ownership qualifier under ARC?
Use of __autoreleasing in code snippet example
尽管现在我明白我仍然无法得到主要的东西,目的。为什么这样?让我解释一下究竟让我困惑的是什么。考虑一下代码:
@interface MyError : NSError
@end
@implementation MyError
- (void)dealloc
{
NSLog(@"My error dealloc");
}
@end
@interface ErrorHandler : NSObject
- (void)handleError:(MyError* __strong*)error;
@end
@implementation ErrorHandler
- (void)handleError:(MyError* __strong*)error
{
*error = [[MyError alloc] initWithDomain:@"Blabla" code:100500 userInfo:@{
NSLocalizedDescriptionKey : @"TestDescription"
}];
}
@end
- (void)test
{
MyError *error = nil;
ErrorHandler *handler = [ErrorHandler new];
[handler handleError:&error];
NSLog(@"Localized description %@", error.localizedDescription);
}
我写了这段代码,看看如果我不使用__autoreleasing
会发生什么。如您所见handleError
方法接受对明确声明为__strong
的引用的引用。没有任何反应。一切都好。我能够从MyError
对象获取信息,并且已经成功解除分配,我明白了。如果我放__autoreleasing
而不是__strong
没有任何变化。那么为什么使用__autoreleasing
如果它什么都没改变呢?这是我不明白的。谁能告诉我我错过了什么?谢谢大家
答案 0 :(得分:11)
我认为tl; dr的回答是,通过将参数声明为__autoreleasing
,您还可以根据需要将指针传递给弱引用。
想象一下你的方法如下:
-(void) handleError: (NSError* __strong *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
编译器认为*error
很强,所以当它完成赋值时,你得到+1保留计数。在编译器完成代码之后,它看起来像这样:
-(void) handleError: (NSError* __strong *) error
{
NSError* myError = [[NSError alloc] init];
*error = [myError retain];
[myError release];
}
所以,如果你这样称呼它:
NSError* error; // strong reference
[self handleError: &error];
一切都很好,因为编译器会在范围的末尾正确地发布版本。如果你这样做:
NSError* __weak error; // weak reference
[self handleError: &error];
它可能无法编译,但如果确实如此,因为编译器认为引用很弱(它无法在handleError:
中看到强大的赋值),它将不会放入一个发布版本和对象会泄漏。
如果你定义这样的方法:
-(void) handleError: (NSError* __weak *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
编译器添加代码,如下所示:
-(void) handleError: (NSError* __weak *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
[myError release];
}
更糟糕的是因为*error
的赋值给出了+0保留计数,这意味着,只要myError
超出范围,即当方法返回时,它指向的对象就会得到解除分配。
如果你这样做:
-(void) handleError: (NSError* __autoreleasing *) error
{
NSError* myError = [[NSError alloc] init];
*error = myError;
}
编译器执行此操作:
-(void) handleError: (NSError* __autoreleasing *) error
{
NSError* myError = [[NSError alloc] init];
*error = [[myError retain] autorelease];
[myError release];
}
与前一种情况相同,但返回的对象位于方法末尾的自动释放池中,因此不会取消分配。因此,您可以将调用者中的error
声明为强或弱,并且编译器有机会对返回的引用做一些合理的事情。
至少我认为这就是问题所在。