NSDictionary 释放触发 valueForKey 返回值释放



我目前正在尝试将自己投入iOS开发。现在我很难理解内存管理。这就是我困惑的原因:

NSString *path = [self.dataPath stringByAppendingPathComponent:@"dummy.plist"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
NSString *dummyKeyValue = [dict valueForKey:@"dummyKey"];
// NSLog(@"%@",[NSString stringWithString:dummyKeyValue]);
[dict release];
NSString *anotherString = [dummyKeyValue lowercaseString];

这段代码在最后一行触发EXC_BAD_ACCESS错误。似乎是因为NSDictionary发布了它的关键值。我不明白的是为什么没有考虑dummyKeyValue定义,因为显然dummyKeyValue仍然指向"dummyKey"的价值。

现在,当您注释掉NSLog行时,会出现下一个问题甚至更有趣的现象。以一种或另一种方式使用dummyKeyValue似乎会阻止释放它所指向的内存。为什么?

感谢帮助!

在手动引用计数模式下,简单地定义一个变量并不意味着变量指向的对象将自动保留。当dict被释放时,它会释放它的值对象,如果没有其他对象对它们的强引用(即它们的引用计数现在为 0),它们就会被释放。这就是你在这里看到的。

如果你想保留对虚拟键值的强引用,你需要在收到它后保留它。当然,这也意味着您需要在完成它后释放它。试试这个:

NSString *dummyKeyValue = [[[dict valueForKey:@"dummyKey"] retain] autorelease];

现在,虚拟键值将一直存在,直到当前自动发布池范围的结束。通常,编写访问器方法是为了在返回值之前执行此操作,以避免您所看到的情况。

值得注意的是,如果您使用的是ARC(自动引用计数),则不会遇到此问题,因为编译器会插入必要的保留/释放调用,以确保dummyKeyValue一直存在,直到您完成它。

这是基本的内存管理。创建 dict 时,字典正在管理它包含的所有键和值的内存。

当您获取值并将其分配给dummyKeyValue变量时,您不会对该值进行任何额外的内存管理。你只需要一个指向对象的变量。

现在,当您释放dict 时,字典也会释放其所有键和值。换句话说,所有键和值的保留计数都减少了 1。由于此时没有其他任何内容保留键和值,因此它们都已解除分配。

此时,您的dummyKeyValue变量指向导致崩溃的已释放值。

您必须选择解决此问题。

  1. 保留存储在 dummyKeyValue 中的值(需要释放)。
  2. 将呼叫移至 [dict release] 在您上次使用 dummyKeyValue 之后

NSLog语句似乎可以解决问题的原因是,您的NSLog会创建一个自动发布的NSString。此NSString保留用于创建它的值。因此,当dict发布时,NSString仍然具有对与dummyKeyValue相同的对象的引用。这允许该对象的寿命更长一点,以防止崩溃。

(您应该使用 objectForKey:NSDictionary中检索内容。

首先,打开 ARC。如果您刚刚开始使用 Cocoa,除了内存管理之外,您还需要学习许多其他关于环境的东西。供应商捆绑的编译器将处理 99% 的内存管理问题,您应该让它这样做。这一直是Cocoa新开发商的主要绊脚石,现在已经被消除了。一旦你掌握了其他所有内容,稍后再回到这个话题。现在,忽略这个答案的其余部分。没有第三步。

也就是说,这就是这里发生的事情:

当你使用objectForKey:从字典中获取一个对象时,你不会获得它的所有权。它只是一个引用,字典仍然拥有该对象。如果你销毁字典,它的所有对象都会随之而去——除非它们也由其他东西拥有(保留)。如果你想让对象在字典被破坏之后存活下来,你需要用retain对它提出声明:

NSString *dummyKeyValue = [[dict objectForKey:@"dummyKey"] retain];

像这样获得所有权后,您现在还负责放弃该所有权,方法是在完成对象时使用release,或者自动释放它,在这种情况下,它将在此方法结束后的某个时间释放。

使用检索到的对象作为参数来stringWithString:使其继续存在,因为显然,作为您无法依赖的内部细节,该方法正在保留和自动释放参数。或者其他你不能依赖的东西。这是一个有趣的发现,但不是管理对象生存期的实用方法。

你应该写:

NSString *dummyKeyValue = [[dict valueForKey:@"dummyKey"] retain];

NSString *dummyKeyValue = [[dict valueForKey:@"dummyKey"] copy];

您引用 objec 并不重要,因为您没有使用 ARC。每个对象都有一个引用计数。您的dummyKeyValue的引用计数为 1,直到您的字典存在。释放它时,引用计数会减少(变为 0),并且对象将被解除分配。所以dummyKeyValue什么都没有指向。

顺便说一句,如果要增加对象的引用计数,请使用 retainrelease 来减少它。

最新更新