3

我目前正在尝试自己进行 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

4 回答 4

3

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

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

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

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

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

于 2013-01-23T20:56:55.337 回答
2

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

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

现在,当您 release 时dict,字典也会释放其所有键和值。换句话说,所有键和值的保留计数都减一。由于此时没有其他任何东西保留了键和值,因此它们都被释放了。

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

您必须选择解决此问题。

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

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

于 2013-01-23T20:56:09.040 回答
2

(您应该使用objectForKey:从 . 中检索东西NSDictionary。)

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

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

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

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

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

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

于 2013-01-23T20:56:42.073 回答
0

你应该写:

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

或者

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

你有一个对象的引用并不重要,因为你没有使用 ARC。每个对象都有一个引用计数。在您dummyKeyValue的字典存在之前,您的 ref 计数为 1。当你释放它时,引用计数减少(变为 0)并且对象被释放。所以dummyKeyValue没有任何意义。

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

于 2013-01-23T20:56:01.583 回答