环境:Mac OS X 10.9,Xcode 5.0.2
我为OS X创建标准Cocoa应用程序并在项目设置中禁用ARC。添加一个按钮“运行”:
文件“AppDelegate.h”:
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (assign) IBOutlet NSWindow *window;
- (IBAction)clickRun:(id)sender;
@end
文件“AppDelegate.m”:
#import "AppDelegate.h"
@implementation AppDelegate
- (IBAction)clickRun:(id)sender
{
NSAutoreleasePool* apool = [[NSAutoreleasePool alloc] init];
NSString* pathToFile = @"/Users/admin/1.txt";
NSError* error;
NSLog(@"[1] Retain count of Error: %lx", [error retainCount]);
NSData* dataOfFile = [NSData dataWithContentsOfFile:pathToFile
options:NSDataReadingMappedIfSafe
error:&error];
NSLog(@"[2] Retain count of Error: %lx", [error retainCount]);
[apool drain];
}
@end
运行程序并首次单击按钮控制台时输出:
“ [1]保留错误计数:0 ” - 当然为零,因为变量'错误'未初始化为
“ [2]保留错误计数:0 ” - 第二个零告诉我们“dataWithContentsOfFile”成功读取文件的方法。
调试器总是说变量'error'键入'nil'。
但是当我第二次点击按钮时,调试器会中断:
NSLog(@“[1]保留错误计数:%lx”,[错误retainCount]);
显示消息“EXC_BAD_ACCESS”,变量'error'包含垃圾,当然[error retainCount]得到分段错误。但这有点奇怪,因为如果方法“dataWithContentsOfFile”成功,它就不会分配变量'error'而且没有正文触摸这个变量。
变量'error'从哪里获取垃圾以及为什么第一次点击不包含垃圾?
当然花了几个小时后我解决了这个问题,强制(重新)初始化'nil':
NSError* error = nil;
但上述问题尚未过期。
答案 0 :(得分:5)
变量error
在堆栈上声明。将强[1]对象引用到堆栈时初始化为nil的特性是仅ARC特征,即如果将其关闭,则该变量将不再被初始化。
但是,堆栈上分配的内存最初是归零的,只有在使用它包含垃圾之后才会归零。此外,垃圾本身可能巧合地为零。第一次输入此方法时,内存可能是新分配的,因此error
恰好是幸运的。在后续调用中,内存可能已被其他堆栈帧使用,因此error
包含垃圾。
其他要点:
将-retainCount
发送到有效对象将永远不会返回0,因为-release
会取消分配对象,而不是在计数为1时递减计数。
当你看到模式时
someResult = [someObject blahBlahError: &error];
技术上不允许使用传递回error
的值,除非someResult
表示发生了错误。在您的示例中,如果error
设置为nil,则应该只尝试使用dataOfFile
。
[1]感谢Nikolai的澄清。
答案 1 :(得分:1)
变量未初始化,指针类型的局部变量不会自动初始化。通常它们将包含以前在堆栈中的位置,因此它是完全随机的,你可以期待任何东西 - 包括nil,就像第一次运行代码时那样。
答案 2 :(得分:1)
第一次运行该方法时,指针error
巧合地是nil
。根据定义,将retainCount
等消息发送到nil
会返回0。不存在任何保留计数为零的对象。
第二次运行该方法时,error
不是nil
。因此,发送消息将是不良访问。