为什么 NSUserDefaults 返回一个已释放的字符串? (更新#2)

发布于 2024-11-18 22:06:47 字数 2448 浏览 3 评论 0原文

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

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

当我在应用程序生命周期内多次调用 this 时,在单步执行此代码后,我最终会在控制台中出现错误:

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

详细信息如下: crash

就像你所看到的,我有一个断点正好位于对 -stringForKey: 的方法调用的行上:NSUserDefaults 的代码>。跨过去之后,车祸就发生了!甚至没有机会阅读 val 中的内容。已解除分配!

我从任何地方放入 NSUserDefaults 中的任何字符串都会发生这种情况,而且我在内存管理方面没有做任何错误。 Leaks Instrument 一切都很完美,与 Clang Static Analyzer 的结果相同。

控制台错误:

*** -[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];

因此即使我不更改键 @"defaultCity 的此字符串,并且我想要一段时间后访问它(当我重新加载该胖视图两次时),我最终从 NSUserDefaults 获得了一个已释放的实例,

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

我已经 100% 确定没有内存泄漏(使用 Leaks 工具进行了过度测试),我不明白为什么会发生这种情况。它们到 NSUserDefaults (无论如何这都是愚蠢的),但没有成功


问题解决了!

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

但奇怪的是,我能够加载、销毁、加载、销毁该视图。然后,它发生在第一次尝试从 NSUserDefaults 读取任何字符串时。这就像一个释放的字符串通过默认字典或默认数据库传播并破坏所有内容,从而使 NSUserDefaults 和 NSDictionary 算法变得疯狂。

纠正了那个错误后,一切正常。还包括对 NSUserDefaults 中所有其他字符串的访问。有效,但仍然是个谜,这个被遗忘的self.如何产生如此大的影响。

感谢所有帮助解决此问题的人。你救了我免于心脏病发作。我离它是如此之近。好吧,现在必须买一些新东西了……我的办公室看起来就像 Goa'uld 袭击后的星门房间。

In my app I write multiple strings to the defaults database, like this:

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

When I call multiple times during my app life time this, then I end up with an error in the console after stepping over this code:

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

Here in more detail:
crash

Like you can see I have a breakpoint exactly on the line with the method call to -stringForKey: of NSUserDefaults. After stepping over, the crash happens already! No chance to even read what is in val. It is deallocated!

This happens with ANY string I put in NSUserDefaults, from ANYWHERE, and I am not doing anything wrong with memory management. Leaks Instrument is all perfect, same as the Clang Static Analyzer results.

Console error:

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

Top of stack trace in debugger after crash: (top line reads forwarding)

stacktrace

Machine code symbols:

arm code

Now the REALLY strange part: I have a view controller which loads a heavy UI. When I destroy this view and load it, and destroy it again and load it again, THEN NSUserDefaults is dead. Crash as soon as I want to read a string from NSUserDefaults. It's absolutely always the exact same behavior.

I thought maybe there is a memory leak that eats up all the RAM. But this happens on a big development machine as well as on the iPad. Release and reload the view two times and NSUserDefault has this defect.

Just to make sure it is not my fault, I re-wrote my NSUserDefaults writing code to something like this:

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

But still, I end up with getting deallocated string instances from NSUserDefaults! On the simulator and on the device! Also, some of those strings I try to access have been written as default when initializing the application in + (void)initialize

NSDictionary *preSettings = [[NSDictionary alloc] initWithObjectsAndKeys:
@"Hollywood", @"defaultCity",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:preSettings];

So even if I do not change this string for the key @"defaultCity and I want to access it after a while (when I reloaded that fat view twice), I end up getting a deallocated instance from NSUserDefaults.

I also tried to re-initialize the whole thing, manually calling my method in the App Delegate which registers that default dictionary with NSUserDefaults. Helps nothing.

I've made 100% sure that there are no memory leaks (tested excessively with the Leaks instrument). I don't get it why this happens. I also tried to retain those strings 5 times before I write them to NSUserDefaults (which would be stupid anyways), with no success.

Ideas?


PROBLEM SOLVED!

One of the strings returned by NSUserDefaults was assigned to a retaining property. I forgot to add a self. in front of self.theProperty = theStringFromNSUserDefaults which resulted in over-releasing that string in -dealloc when the view got deallocated.

Strange though, that I was able to load, destroy, load, destroy that view. And then it happened upon the first attempt of reading any string from NSUserDefaults. It's like a deallocated string propagates through the defaults dictionary or defaults database and tears everything down, driving the NSUserDefaults and NSDictionary algorithms notally nuts.

After correcting that single error everything worked fine. Including access to all other strings in NSUserDefaults as well. Works, but still a mystery how this one single forgotten self. had such a big impact.

Thank you everyone who helped solving this issue. You saved me from a heart attack. I was so close to it. Well, must buy some new stuff now... my office looks like the Star Gate room after a Goa'uld attack.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

萤火眠眠 2024-11-25 22:06:47

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% 准确,也不能检测到所有泄漏。如果任何地方都有对对象的引用,则不会被视为泄漏。

我认为可能存在内存泄漏
这会耗尽所有内存。但这
发生在大型开发机器上
以及在 iPad 上。释放并
重新加载视图两次并
NSUserDefault 就有这个缺陷。

查克的理论很可能是正确的。最有可能的是,你有这样的东西:

id foo = ... some object ...
[foo 发布];
...对 NSUserDefaults 做一些事情,以便它恰好分配一个现在悬空的 foo 指向的对象...
[foo 发布];
.... 哎呀;现在用户默认值中的对象已被释放

很容易弄清楚;在 Xcode 运行面板的 Diagnostics 选项卡中打开“Malloc Stack Logging”。然后,当崩溃发生时,执行 info malloc

并且在该地址发生的所有 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

The only way that NSUserDefaults could return a deallocated string is if you over-released a string that happened to be in the user defaults cache in the first place. I.e. I'd bet if you do something like:

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

You'd end up with a deallocated string reference in val. What are you doing with val throughout your code?


Or, as Chuck said, this could be a dangling pointer issue, too. I.e. you have a dangling pointer that happens to then point to the object in the user defaults and it gets released.


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

You are leaking the copy of theString in that code.

I've made 100% sure that there are no
memory leaks (tested excessively with
the Leaks instrument). I don't get it
why this happens. I also tried to
retain those strings 5 times before I
write them to NSUserDefaults (which
would be stupid anyways), with no
success.

The leaks instrument is neither 100% accurate, nor can it detect all leaks. If there is a reference to an object anywhere, it isn't counted as a leak.

I thought maybe there is a memory leak
that eats up all the RAM. But this
happens on a big development machine
as well as on the iPad. Release and
reload the view two times and
NSUserDefault has this defect.

Chuck's theory is most likely correct; most likely, you have something like:

id foo = ... some object ...
[foo release];
... do something with NSUserDefaults such that it happens to allocate an object where the now-dangling foo points to ...
[foo release];
.... oops; now the object in the user defaults has been deallocated

Easy to figure out; turn on "Malloc Stack Logging" in the Diagnostics tab of the run panel in Xcode. Then, when the crash happens, do info malloc <address of bad thing> and all of the malloc/free events that happened at that address will be spewed. Most likely you'll see something like:

- 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
无法言说的痛 2024-11-25 22:06:47

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

If your application needs to maintain a reference to val beyond the scope of the current method, you need to retain it also. If you don't, it may get autoreleased before you try to access it.

陌上芳菲 2024-11-25 22:06:47

您试图在哪里保留字符串? NSUserDefaults 返回一个自动释放的 NSString。是否有可能在自动释放池清理它之前不对其执行任何操作?

Where are you attempting to retain the string? NSUserDefaults returns an autoreleased NSString. Is it possible that you don't do anything with it until the Autorelease Pool has cleaned it up?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文