我目前正在尝试自己开发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
似乎会阻止释放它所指向的内存。为什么?
非常感谢帮助!
答案 0 :(得分:3)
在手动引用计数模式中,简单地定义变量并不意味着变量指向的对象将自动保留。释放dict
时,它会释放其值对象,如果没有其他对象具有强引用(即它们的引用计数现在为0),则释放它们。这就是你在这里看到的。
如果你想保持对dummyKeyValue的强引用,你需要在收到它后保留它。当然,这也意味着你需要在完成后释放它。试试这个:
NSString *dummyKeyValue = [[[dict valueForKey:@"dummyKey"] retain] autorelease];
现在,dummyKeyValue将一直存在,直到当前自动释放池范围结束。通常,写入访问器方法是为了在返回值之前执行此操作,以避免您正在看到的情况。
值得注意的是,如果您使用ARC(自动引用计数),则不会出现此问题,因为编译器会插入必要的保留/释放调用以确保dummyKeyValue在您完成之前一直停留。< / p>
答案 1 :(得分:2)
这是基本的内存管理。创建dict
时,字典管理其包含的所有键和值的内存。
当您获得一个值并将其分配给dummyKeyValue
变量时,您不会对该值进行任何额外的内存管理。你只需要一个指向一个对象的变量。
现在,当您释放dict
时,字典也会释放其所有键和值。换句话说,所有键和值的保留计数都减少了一个。因为,在这一点上,没有其他任何东西保留了键和值,它们都被解除分配。
此时,您的dummyKeyValue
变量指向一个导致崩溃的解除分配值。
您必须选择解决此问题。
dummyKeyValue
中存储的值(需要释放)。[dict release]
之后将电话转移到dummyKeyValue
。 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
指向什么都没有。
顺便说一句,如果你想增加一个对象的引用计数,可以使用retain
和release
来减少它。