【问题标题】:Objective C NSMutableDictionary memory managementObjective C NSMutableDictionary 内存管理
【发布时间】:2011-03-31 23:15:55
【问题描述】:

我有一个模型类,用于记录由多个视图构建的跟踪记录。它有一个 NSMutableDictionary,其中包含我最终写入数据库的字段和值。它被保存到 plist 并在需要时加载回来。我以为我在跟踪我的记忆,但是当我尝试释放字典时它会抛出一个 EXC_BAD_ACCESS。这是我的界面:

#import <Foundation/Foundation.h>


@interface CurrentEntryModel : NSObject {
 NSMutableDictionary *currentEntry;
}

@property (nonatomic, retain) NSMutableDictionary *currentEntry;
- (void) setValue: (NSString *)value;
- (NSString *) getValue;

@end

我的理解是 currentEntry 应该被保留,我必须在 dealloc 期间释放它。

这是我的实现(这不是整个类,只是相关部分):

#import "CurrentEntryModel.h"


@implementation CurrentEntryModel

@synthesize currentEntry;

-(id) init {
    if ( self = [super init] )
    {
    //check for file
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *file;
    file = @"location.plist";

    if ([fileManager fileExistsAtPath:file]){ 
        NSLog(@"file exists");
        currentEntry = [[NSMutableDictionary alloc] initWithContentsOfFile:file];

    }else {
        NSLog(@"file doesn't exist");
        currentEntry = [[NSMutableDictionary alloc ] initWithCapacity:1];

        NSDate *testDate = [NSDate date];

        [currentEntry setObject:testDate forKey:@"created"];

        [currentEntry writeToFile:file atomically:YES];

    }

}
return self;
}

- (void) setValue: (NSString *)value {
[currentEntry setObject:value forKey:@"location"];
}

- (NSString *) getValue {
return [currentEntry objectForKey:@"location"];
}


- (void) dealloc{
[currentEntry release];
[super dealloc];

}

@end

如果我初始化这个类,它会自动创建字典,如果我调用 set 或 get 方法之一,它似乎会保留字典,因为它会正确解除分配。如果类刚刚初始化,然后没有调用任何方法,它将抛出 EXC_BAD_ACCESS 错误。如果文件不存在时我没有弄错,我没有正确初始化字典,因为该方法以字典而不是 init 开头。虽然每次我运行这个文件都在那里,所以它总是使用文件找到的逻辑,我认为这会保留变量。

我没有正确初始化字典吗?

编辑 - 更改了便捷方法的代码以反映正确的方法。大家注意一下 Squeegy 说的话。

【问题讨论】:

  • 我很欣赏有关类方法的信息,但我已经在我的问题中声明这不是错误的来源 - Although every time I run this the file is there so it always uses the the file found logic and I thought that that will retain the variable。有人有什么想法吗?

标签: iphone objective-c xcode memory-management


【解决方案1】:

这很糟糕。

else {
        NSLog(@"file doesn't exist");
        currentEntry = [[NSMutableDictionary alloc ] dictionaryWithCapacity:1];

dictionaryWithCapacity:NSMutableDictionary 上的一个类方法,它返回一个自动释放的对象,而你没有retain 它。所以运行循环结束,字典被自动释放。然后你在你的dealloc 中运行[currentEntry release],它会爆炸,因为该对象已经被释放了。

你可能不想initWithCapacity:。始终将alloc 与以init 开头的方法配对。


另外,当使用像这样的保留属性时,我通常让属性为我解决这个问题,并且只使用自动释放的对象。你只需要记住更少的规则,就会有更少的陷阱。

- (id)init {
  // ...
  self.currentEntry = [NSMutableDictionary dictionWithContentsOfFile:file];
  // ...
}

- (void)dealloc {
  //...
  self.currentEntry = nil;
  //...
}

这样您就不必直接在对象上调用retainrelease。根据我的经验,这会减少令人困惑的错误。但这也是许多 ObjC 程序员的风格点,并不是每个人都同意。

【讨论】:

  • 是的,我今天发现了这一点,并在研究答案时在我的问题中引用了它。我阅读的大多数教程都没有引用自动释放的便捷方法,您需要使用以 init 开头的东西。我确实改变了它,它仍然抛出 EXC_BAD_ACCESS 错误。
  • 来自内存管理规则:“就像您不应该关心对象的实际保留计数一样,您也不应该关心返回给您的对象是否自动释放。唯一的问题是,你是否拥有它。”假设返回给您的对象是否是自动释放的有点不利。特别是,[NSNumber numberWithInt:0] 会给你一个你不拥有的对象,但通常也不会自动释放(它通常被缓存)。
  • 这很好。 initretain 或属性分配都意味着“我拥有这个”。
  • 我已经编辑了问题以反映这一点,但它仍然没有回答问题。即使使用 init 方法,它仍然会抛出 EXC_BAD_ACCESS。我的理解是应该保留这个。
【解决方案2】:

约书亚-

+ (id)dictionaryWithCapacity:(NSUInteger)numItems

是 NSDictionary 的一个类方法。所以当你调用它时,它应该是:

[NSMutableDictionary dictionaryWithCapacity:1];

不是:

[[NSMutableDictionary alloc] dictionaryWithCapacity:1];

此外,[NSMutableDictionary dictionaryWithCapacity:] 返回一个自动释放的对象。如果您想将字典保留为 ivar 并且不让它在运行循环的下一个周期自动释放,您应该调用:

[currentEntry retain];

所以,基本上,将其更改为:

currentEntry = [[NSMutableDictionary alloc] initWithCapacity:1];

或:

currentEntry = [[NSMutableDictionary dictionaryWithCapacity:1] retain];

第一个可能更有意义,因为便利类方法被设计为在您需要自动释放实例时使用。

【讨论】:

  • 不要假设从类方法返回的对象是否“自动释放”,而应该只关心您是否拥有该对象。来自内存管理规则:“就像您不应该关心对象的实际保留计数一样,您也不应该关心返回给您的对象是否是自动释放的。唯一的问题是,您是否拥有它或不是。”
猜你喜欢
  • 1970-01-01
  • 2020-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-08
相关资源
最近更新 更多