更新:这已在 iOS 6.1 DP3 SDK 中修复。
在使用默认发布构建配置(调试似乎工作正常)使用 ARC 构建时,我发现了一个释放后使用的崩溃。当在具有非常量条件的 if 范围内创建对象,将其分配给范围外的变量,然后仅使用 Objective-C 数组或字典文字引用该变量时,就会出现问题。
这是我设法找到的最小的可重现案例:
void test(BOOL arg)
{
id obj = nil;
if (arg) {
obj = [NSObject new];
}
// obj already deallocated here
@[obj];
// but using NSArray works
//[NSArray arrayWithObject:obj];
// @[obj] works if obj is referenced i.e. by NSLog print out
//NSLog(@"%@", obj);
}
int main(int argc, const char * argv[])
{
@autoreleasepool {
test(YES);
}
return 0;
}
当我在启用僵尸对象的情况下构建和运行它时,我收到以下错误消息:
-[NSObject retain]: message sent to deallocated instance 0x100109100
正如我在代码中评论的那样,如果obj
以其他方式引用它,它可以正常工作,比如 withNSLog
或 using NSArray
。我是否误解了如何使用 ARC 和作用域释放对象,或者这是 LLVM 或 Clang 中的优化错误?
我正在使用 Xcode 4.5.2 和 clang 版本 4.1 (tags/Apple/clang-421.11.66) (基于 LLVM 3.1svn)。我可以在为 iOS 模拟器和 Mac OS X 构建 x86 64 位时重现它,并且我很确定在为 ARM 构建时会出现同样的问题,因为在 iPhone 上运行发布构建时首次发现了该问题。
我已经向 Apple 提交了错误报告并创建了一个开放的雷达报告。
如果有的话,我错过了什么?
更新,做了一些更多的实验:
正如 Gabro 指出的那样,编译器会转换@[]
为[NSArray arrayWithObjects:count:]
语句,所以我做了一些测试:
// works
id a[] = {obj};
[NSArray arrayWithObjects:a count:1];
// does not work
const id *b = (id[]){obj};
[NSArray arrayWithObjects:b count:1];
// does not work
[NSArray arrayWithObjects:(id[]){obj} count:1];
所以我的猜测是,当结合 ARC 和匿名 C 数组时会发生这种情况。