NSDictionary释放触发valueForKey返回值释放

时间:2013-01-23 20:47:16

标签: ios objective-c memory-management nsdictionary nsmutabledictionary

我目前正在尝试自己开发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似乎会阻止释放它所指向的内存。为什么?

非常感谢帮助!

4 个答案:

答案 0 :(得分:3)

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

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

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

现在,dummyKeyValue将一直存在,直到当前自动释放池范围结束。通常,写入访问器方法是为了在返回值之前执行此操作,以避免您正在看到的情况。

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

答案 1 :(得分:2)

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

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

现在,当您释放dict时,字典也会释放其所有键和值。换句话说,所有键和值的保留计数都减少了一个。因为,在这一点上,没有其他任何东西保留了键和值,它们都被解除分配。

此时,您的dummyKeyValue变量指向一个导致崩溃的解除分配值。

您必须选择解决此问题。

  1. 保留dummyKeyValue中存储的值(需要释放)。
  2. 在上次使用[dict release]之后将电话转移到dummyKeyValue
  3. NSLog语句似乎可以解决问题的原因是NSLog创建了自动释放的NSString。此NSString保留您用于创建它的值。因此,当dict被释放时,NSString仍然会引用与dummyKeyValue相同的对象。这使得该对象能够存活一段时间以防止崩溃。

答案 2 :(得分:2)

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

首先,打开ARC。如果你刚开始使用Cocoa,那么除了内存管理之外,你还需要学习很多关于环境的知识。供应商捆绑的编译器将处理99%的内存管理问题,您应该放弃它。对于新的Cocoa开发人员来说,这一直是一个主要的绊脚石,现在已经被淘汰了。一旦掌握了其他所有内容,请稍后再回到这个主题。现在,忽略这个答案的其余部分。没有第三步。

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

使用objectForKey:从字典中获取对象时,您无法获得对象的所有权。它只是一个参考,字典仍然拥有该对象。如果你销毁字典,它的所有对象都会随之而来 - 除非它们也被别的东西所拥有(保留)。如果您希望对象超越字典的销毁,您需要使用retain对其进行声明:

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

拥有这样的所有权后,您现在还有责任放弃该所有权,或者在完成对象时使用release,或者通过自动释放它来放弃所有权,在这种情况下,它将在之后的某个时间发布这种方法的结束。

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

答案 3 :(得分:0)

你应该写:

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

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

由于您没有使用ARC,因此引用目标并不重要。每个对象都有一个引用计数。在您的字典存在之前,dummyKeyValue的引用计数为1。释放它时,引用计数减少(变为0)并取消分配对象。所以dummyKeyValue指向什么都没有。

顺便说一句,如果你想增加一个对象的引用计数,可以使用retainrelease来减少它。