为什么未初始化的变量包含垃圾而不是'nil'?

时间:2014-01-10 16:15:15

标签: objective-c xcode memory initialization

环境: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;

但上述问题尚未过期。

3 个答案:

答案 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。因此,发送消息将是不良访问。