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

ope*_*rog 9 iphone memory-management objective-c nsuserdefaults ios

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

[[NSUserDefaults standardUserDefaults] setObject:@"Hi" forKey:@"GREETING"];
Run Code Online (Sandbox Code Playgroud)

当我在我的应用程序生命周期内多次调用时,我在踩过这段代码后最终在控制台中出现错误:

NSString *val = [[NSUserDefaults standardUserDefaults] objectForKey:@"someKey"];
Run Code Online (Sandbox Code Playgroud)

在这里更详细: 紧急

就像你可以看到我有完全与方法调用线断点-stringForKey:NSUserDefaults.踩到后,崩溃已经发生了!甚至没有机会阅读其中的内容val.它被解除分配!

我在任何地方放入NSUserDefaults中的任何字符串都会发生这种情况,我对内存管理没有任何错误.泄漏仪器完美无缺,与Clang Static Analyzer结果相同.

控制台错误:

*** -[CFString retain]: message sent to deallocated instance 0x610ba10
Run Code Online (Sandbox Code Playgroud)

崩溃后调试器中的堆栈跟踪顶部:(顶行读取转发)

堆栈跟踪

机器代码符号:

武装代码

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

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

为了确保这不是我的错,我重新编写了我的NSUserDefaults编写代码,如下所示:

[[NSUserDefaults standardUserDefaults] setObject:[theString copy] forKey:key];
Run Code Online (Sandbox Code Playgroud)

但是,我最终还是从NSUserDefaults获取了已释放的字符串实例!在模拟器和设备上!此外,我尝试访问的一些字符串在初始化应用程序时已被写为默认值+ (void)initialize

NSDictionary *preSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
@"Hollywood", @"defaultCity",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:preSettings];
Run Code Online (Sandbox Code Playgroud)

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

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

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

想法?


问题解决了!

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

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

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

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

bbu*_*bum 10

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

p = [NSAutoreleasePool new];
NSString *val = [[NSUserDefaults standardUserDefaults] objectForKey:@"someKey"];
[val release];
[p drain];
val = [[NSUserDefaults standardUserDefaults] objectForKey:@"someKey"];
Run Code Online (Sandbox Code Playgroud)

你最终会在val中找到一个deallocated字符串引用.你在val整个代码中做了什么?


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


[[NSUserDefaults standardUserDefaults] setObject:[theString copy] forKey:key];
Run Code Online (Sandbox Code Playgroud)

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

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

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

我想也许有一个内存泄漏会占用所有内存.但这种情况发生在一台大型开发机器上以及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
Run Code Online (Sandbox Code Playgroud)

  • NSUserDefaults字符串也可能被悬空指针过度释放.如果将自动释放的对象分配给应该保留的位置,并且在其位置分配NSUserDefaults字符串,则当解除分配的对象的"所有者"尝试释放它时,它将释放该字符串. (2认同)