苹果在ARC文档中对通过引用的警告是什么



在苹果关于ARC的文档中,他们强调指出了一个有问题的场景,即ARC将在幕后生成一个样板临时变量。搜索"编译器因此重写":

https://developer.apple.com/library/mac/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html

警告的要点似乎是,由于基于堆栈的变量是"强"的,并且被调用方法的by reference参数(performOperationWithError:)是自动释放的,ARC将生成一个临时本地变量来满足自动释放变量的内存管理需求。但是,因为在样板示例中,临时变量被分配给了强变量,所以从客户的角度来看,似乎没有风险。

在这里,文档煞费苦心地警告我们到底是什么?作为一个客户端或一个方法的实现者,可能会以这种方式调用(使用一个自动发布的、按值返回的参数),风险是什么?

这只是关于性能不理想的警告。在重写的代码中,"tmp"指向的NSError会自动释放,在分配给"error"时会被保留,然后在"error"超出范围时再次释放。

如果将原始代码中的声明更改为:NSError __autoreleaseing*错误;

如果这样做,就不会对临时对象进行赋值,也不会再发生隐式的保留然后释放。(NSError对象本身仍然和以前一样有效,因为它仍然在自动发布池中。)因此,文档警告您,如果使用"错误"的变量限定符,它可能会导致额外的保留计数,而这在其他情况下是不需要的。

还要注意的是,对于任何一个版本的代码:因为有问题的变量是通过引用传递的,而不是-performOperationWithError:的返回值,所以没有机会像ARC那样完成神奇的堆栈遍历技巧,从一开始就避免对象进入自动释放池。

如果您开始查看传递到方法中的值,我认为这是为了防止混淆。在他们的例子中,如果我在调用[myObject performOperationWithError:&tmp];并键入p error的行上设置一个断点,我会看到它的地址。但如果我进入-performOperationWithError:并键入p error,我会得到一个不同的值——在方法内部,error指向那个临时值。

我可以看到这样一种情况,一些糟糕的sap试图用ARC调试一些棘手的东西,在这种情况下,指针在传递到方法中时发生变化将是一种非常令人困惑的转移注意力的方法。

我的猜测:如果您对输出参数引用的内存进行假设,例如索引掉指针,您可能会感到惊讶。

我认为这与客户端没有任何关系。它似乎引用了WWDC 2013视频中关于内存问题的相同问题:如果您自己实现一个采用自动释放间接参数(如NSError**)的方法,并且如果您在该方法中创建了一个自动释放池块,请不要从自动释放池中分配NSError。相反,分配给本地变量,然后从本地分配给自动释放池块外的NSError。

在我看来,这与其说是对这种行为的警告,不如说是对编译器在这种情况下所做操作的描述,以及为什么你可以将强本地错误引用的地址传递给一个声明为需要__autoreleaseing引用而不触发投诉的方法。

您通常希望API在这样的参数上使用__autoreleaseing,以防ARC或非ARC代码正在使用它,因为在非ARC代码中,必须释放这样的输出参数是不寻常的。

苹果文档引用了一个编译器misfeature,它将为您合成一个临时变量,以处理__block和__autorelasing之间的转换。遗憾的是,这并不能解决很多问题,而且会产生潜在的灾难性意外结果。

例如:

int main(int argc, char *argv[])
{
__block id value = @"initial value";
void (^block)(id *outValue) = ^(id *outValue){
value = @"hello";
};
block(&value);
NSLog(@"value = %@", value);
return 0;
}

对于ARC,报告如下:

2013-04-24 13:55:35.814 block-local-address[28013:707] value = initial value

但使用MRR:

2013-04-24 13:57:26.058 block-local-address[28046:707] value = hello

使用NSFileCoordinator时经常会出现这种情况,导致您丢失由此产生的NSError!

#import <Foundation/Foundation.h>
int main(int argc, char *argv[])
{
NSURL *fileURL = [NSURL fileURLWithPath:@"/tmp/foo"];
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
__block NSError *error;
[coordinator coordinateWritingItemAtURL:fileURL options:0 error:&error byAccessor:^(NSURL *newURL){
NSDictionary *userInfo = @{
NSLocalizedDescriptionKey : @"Testing bubbling an error out from a file coordination block."
};
error = [NSError errorWithDomain:NSPOSIXErrorDomain code:ENOSYS userInfo:userInfo];
}];
NSLog(@"error = %@", error);
}

使用ARC编译时,这将导致零错误!

这在llvm.org上已经被写为一个bug有一段时间了,尽管我只是更改了标题,以更清楚地表明我建议删除该功能。该bug还附带了一个补丁,用于添加新的标志-fno-objc-arc-writeback以禁用该功能)。

最新更新