【问题标题】:Why uninitialized variable contains garbage instead of ‘nil’?为什么未初始化的变量包含垃圾而不是“nil”?
【发布时间】:2014-01-29 17:05:24
【问题描述】:

环境: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] Retain count of Error: 0" – 第二个零告诉我们什么方法“dataWithContentsOfFile”成功读取文件。

调试器总是说什么变量“error”的类型是“nil”。

但是当我第二次点击按钮时,调试器会中断:

NSLog(@"[1] Retain count of Error: %lx", [error retainCount]);

并显示消息“EXC_BAD_ACCESS”和变量“错误”包含垃圾,当然 [error retainCount] 就像分段错误一样。但这有点奇怪,因为如果方法“dataWithContentsOfFile”成功,它不会分配变量“错误”,也没有主体接触这个变量。

变量“error”从哪里获取垃圾,为什么第一次点击不包含垃圾?

当然花了几个小时后我解决了这个问题,强制(重新)初始化'nil':

NSError* error = nil;

但是上面的问题没有过期。

【问题讨论】:

  • 那么你为什么忽略编译器关于未初始化变量的警告?
  • @Hot Licks 1 XCode 产生了很多警告,我已经被警告弄得眼花缭乱;)在 MS Visual Studio C++ 中,对我来说更整齐地推挤警告。 2 我在某处读到;)如果需要,dataWithContentsOfFile 会自动分配“错误”变量。但我不知道哪些指针类型的未初始化局部变量包含来自堆栈的垃圾。
  • 作为一般规则,无论可能为 ARC 声明什么或其他什么,当您声明一个局部变量时,您应该在“读取”它之前显式地为其分配一个值。在创建它们时不初始化所有本地变量的唯一原因(除了很小的性能影响)是有时“未初始化变量”警告实际上有助于告诉您错过了什么。
  • 我也混淆了调试器,它通过第一次单击(运行)显示变量“error”的类型为“nil”并包含 NSObject。我相信“nil”类型的含义不仅仅是归零。
  • 一如既往,whentouseretaincount.com

标签: objective-c xcode memory initialization


【解决方案1】:

变量error 在堆栈上声明。将 strong[1] 对象引用在堆栈上时初始化为 nil 的功能是 ARC 唯一功能,即如果您将其关闭,则变量将不再被初始化。

然而,分配在堆栈上的内存一开始是归零的,只有在使用后才包含垃圾。此外,垃圾本身可能巧合地为零。第一次输入此方法时,内存可能是新分配的,因此 error 碰巧是 nil 。在随后的调用中,内存可能已被其他堆栈帧使用,因此error 包含垃圾。

其他要点:

向有效对象发送-retainCount 将永远不会返回0,因为-release 会释放对象,而不是在计数为1 时递减计数。

当你看到图案时

 someResult = [someObject blahBlahError: &error];

技术上不允许使用传递回error 的值,除非someResult 表明发生了错误。在您的示例中,您应该仅在 dataOfFile 设置为 nil 时尝试使用 error

[1] 感谢 Nikolai 的澄清。

【讨论】:

  • +1 — 只是注意:即使是第一次进入方法时,堆栈上的内存也不太可能归零。内存页面在分配时归零的事实是正确的。但是以前没有使用堆栈内存的可能性很小。只有当没有其他函数比当前函数更深入堆栈时才会发生这种情况。
  • @NikolaiRuhe 没错,但在这种情况下,我们显然确实在第一次将内存归零。
  • 另外:ARC 只保证 __strong 变量的零初始化。
  • @JeremyP 那是巧合,可以随时更改。
  • @JeremyP Still +1 :) — 特别是指出 Cocoa 中正确的错误引用处理。
【解决方案2】:

变量未初始化,指针类型的局部变量未自动初始化。通常,它们会在其所在位置包含以前在堆栈中的内容,因此它是完全随机的,您可以期待任何内容 - 包括 nil,因为它会在代码第一次运行时发生。

【讨论】:

    【解决方案3】:

    第一次运行该方法时,指针error 巧合的是nil。根据定义,向nil 发送诸如retainCount 之类的消息会返回0。并非存在保留计数为零的任何对象。

    您第二次运行该方法时,巧合的是,error 不是nil。因此,发送消息将是错误的访问。

    【讨论】:

      猜你喜欢
      • 2019-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-02
      • 1970-01-01
      相关资源
      最近更新 更多