为什么NSUserDefaults返回一个解除分配的字符串? (更新#2)

时间:2011-07-06 18:54:21

标签: iphone objective-c ios memory-management nsuserdefaults

在我的应用程序中,我将多个字符串写入默认数据库,如下所示:

[[NSUserDefaults standardUserDefaults] setObject:@"Hi" forKey:@"GREETING"];

当我在我的应用有效期间多次拨打电话时,我在踩过这段代码后最终在控制台中出现错误:

NSString *val = [[NSUserDefaults standardUserDefaults] objectForKey:@"someKey"];

更详细地说: crash

就像你可以看到我在-stringForKey: NSUserDefaults的{​​{1}}方法调用的确切线上有一个断点。踩到后,崩溃已经发生了!甚至没有机会阅读val中的内容。它已被解除分配!

我在任何地方放入NSUserDefaults中的任何字符串都会发生这种情况,我对内存管理没有任何错误。泄漏仪器非常完美,与Clang静态分析仪的结果相同。

控制台错误:

*** -[CFString retain]: message sent to deallocated instance 0x610ba10

崩溃后调试器中的堆栈跟踪:(顶行显示转发

stacktrace

机器代码符号:

arm code

现在真正奇怪的部分:我有一个视图控制器,它加载了一个沉重的UI。当我摧毁这个视图并加载它并再次销毁它并再次加载时,那么NSUserDefaults已经死了。一旦我想从NSUserDefaults读取字符串就崩溃。它绝对是完全相同的行为。

我想也许有一个内存泄漏会占用所有内存。但这种情况发生在一台大型开发机器上以及iPad上。释放并重新加载视图两次,NSUserDefault有此缺陷。

为了确保这不是我的错,我重新编写了我的NSUserDefaults编写代码到这样的代码:

[[NSUserDefaults standardUserDefaults] setObject:[theString copy] forKey:key];

但是,我最终还是从NSUserDefaults获取了解除分配的字符串实例!在模拟器和设备上!此外,在+ (void)initialize

中初始化应用程序时,我尝试访问的某些字符串已被写为默认值
NSDictionary *preSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
@"Hollywood", @"defaultCity",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:preSettings];

所以,即使我没有为key @“defaultCity更改此字符串,我想在一段时间后访问它(当我重新加载那个胖视图两次)时,我最终从NSUserDefaults获取一个释放的实例。

我还尝试重新初始化整个事情,在App Delegate中手动调用我的方法,该方法使用NSUserDefaults注册该默认字典。没有任何帮助。

我已100%确定没有内存泄漏(使用Leaks仪器进行过度测试)。我不明白为什么会这样。在我将它们写入NSUserDefaults之前,我还试图保留这些字符串5次(反正这很愚蠢),但没有成功。

想法?


问题解决了!

NSUserDefaults返回的其中一个字符串已分配给保留属性。我忘了在self.前面添加self.theProperty = theStringFromNSUserDefaults,这导致在视图被解除分配时在-dealloc中过度释放该字符串。

奇怪的是,我能够加载,破坏,加载,破坏该视图。并且然后它发生在第一次尝试从NSUserDefaults读取任何字符串时。这就像一个解除分配的字符串通过默认字典或默认数据库传播并撕下一切,驱动NSUserDefaults和NSDictionary算法非常疯狂。

纠正单个错误后一切正常。包括访问NSUserDefaults中的所有其他字符串。作品,但仍然是一个谜,这一个单身遗忘self.如何产生如此巨大的影响。

感谢所有帮助解决此问题的人。你让我免于心脏病发作。我非常接近它。那么,现在必须购买一些新东西......在Goa'uld袭击之后,我的办公室看起来就像是星门室。

3 个答案:

答案 0 :(得分:10)

NSUserDefaults可以返回释放的字符串的唯一方法是,如果您首先过度释放了恰好位于用户默认缓存中的字符串。即如果你这样做,我敢打赌:

p = [NSAutoreleasePool new];
NSString *val = [[NSUserDefaults standardUserDefaults] objectForKey:@"someKey"];
[val release];
[p drain];
val = [[NSUserDefaults standardUserDefaults] objectForKey:@"someKey"];

你最终会在val中找到一个解除分配的字符串引用。您在整个代码中使用val做了什么?


或者,正如查克所说,这也可能是一个悬垂的指针问题。即你有一个悬空指针,然后指向用户默认值中的对象并且它被释放。


[[NSUserDefaults standardUserDefaults] setObject:[theString copy] forKey:key];

您正在泄露该代码中theString的副本。

  

我已100%确定没有   内存泄漏(过度测试   泄漏工具)。我不明白   为什么会这样。我也试过   在我之前保留这些琴弦5次   将它们写入NSUserDefaults(其中   反正会是愚蠢的),没有   成功。

泄漏仪器既不是100%准确,也不能检测到所有泄漏。如果有对象任何地方的引用,则不会将其视为泄漏。

  

我想也许有内存泄漏   这吃掉了所有的RAM。但是这个   发生在一台大型开发机器上   以及在iPad上。发布和   重新加载视图两次   NSUserDefault有这个缺陷。

查克的理论很可能是正确的;最有可能的是,你有类似的东西:

id foo = ...某个对象......    [foo release];    ...对NSUserDefaults做一些事情,以便它碰巧分配一个现在悬挂的foo指向的对象......    [foo release];    .... oops;现在用户默认值中的对象已被解除分配

易于弄清楚;在Xcode的运行面板的Diagnostics选项卡中打开“Malloc Stack Logging”。然后,当崩溃发生时,执行info malloc <address of bad thing>并且将发出在该地址发生的所有malloc / free事件。很可能你会看到类似的东西:

- malloc
- free
.... repeated some # of times ....
- malloc of some object that you create
- free of same
- malloc of a string by NSUserDefaults
- [inadvertent] free of same

答案 1 :(得分:2)

如果您的应用程序需要保留对当前方法范围之外的val的引用,则还需要retain它。如果不这样做,它可能会在您尝试访问之前自动释放。

答案 2 :(得分:0)

你在哪里试图保留字符串? NSUserDefaults会返回自动释放的NSString。在自动释放池清理完之后,您是否可能不对其进行任何操作?