2011-03-31 54 views
0

我有一个模型类,保持跟踪记录由多个视图构建。它有一个NSMutableDictionary,它有我最终写入数据库的字段和值。它被保存到plist并在需要时装回。我以为我记录了我的记忆,但是当我尝试发布字典时,它会抛出EXC_BAD_ACCESS。这是我的接口:Objective C NSMutableDictionary内存管理

#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 

如果我初始化这个类会自动创建字典,如果我叫组的一个或得到它看起来像字典保留,因为它会正确释放。如果该类刚刚初始化,然后没有方法被调用,它将抛出EXC_BAD_ACCESS错误。如果我没有错误,当文件不存在时,我不正确地初始化字典,因为该方法以字典而不是初始化开始。虽然每次运行该文件时都会使用该文件,但我认为这将保留该变量。

我不正确初始化字典吗?

编辑 - 更改便利方法上的代码以反映正确的方式。每个人都注意到Squeegy必须说的话。

+0

我欣赏关于类方法的信息,但是我在我的问题中已经说过,这不是错误的来源 - “虽然每次运行此文件时都会使用文件找到的逻辑,而我认为这将保留变量'。有没有人有任何想法? – Joshua 2011-04-07 01:51:01

回答

2

这是坏坏坏。

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程序员的风格,并非所有人都同意。

+0

是的,我今天发现了这一点,并在研究答案时引用了我的问题。我读过的大多数教程都没有提及便捷方法autorelease,而且您需要使用以init开头的内容。我确实改变了这一点,它仍然会抛出EXC_BAD_ACCESS错误。 – Joshua 2011-03-31 23:31:15

+0

从内存管理规则:“就像你不应该关心对象的实际保留数量一样,你不应该关心返回给你的对象是否被自动释放,唯一的问题是,你是否拥有它或不。”假定返回给你的对象是自动释放的,这是有点不利的。特别是,'[NSNumber numberWithInt:0]'会给你一个你不拥有的对象,但是一个通常不会自动释放的对象(它通常被缓存)。 – dreamlax 2011-03-31 23:35:29

+0

这是一个很好的观点。 'init','retain'或者属性赋值都意味着“我拥有这个”。 – 2011-04-01 00:23:56

0

约书亚 -

+ (id)dictionaryWithCapacity:(NSUInteger)numItems 

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

[NSMutableDictionary dictionaryWithCapacity:1]; 

不:

[[NSMutableDictionary alloc] dictionaryWithCapacity:1]; 

此外,[的NSMutableDictionary dictionaryWithCapacity:]返回一个自动释放的对象。如果你想保持字典作为伊娃,而不是让它自动释放对运行循环的下一个循环,你应该叫:

[currentEntry retain]; 

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

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

或:

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

第一种可能更有意义,因为设计的connivence类的方法,当你想要一个自动释放的情况下使用。

+0

不要对从类方法返回的对象是否是“自动释放”做出假设,而应该只关心您是否拥有该对象。根据内存管理规则:“正如你不应该关心对象的实际保留计数一样,你不应该关心返回给你的对象是否被自动释放,唯一的问题是,你拥有它还是不。” – dreamlax 2011-03-31 23:32:39