【问题标题】:Memory problems with NSMutableDictionary, causing NSCFDictionary memory leaksNSMutableDictionary 的内存问题,导致 NSCFDictionary 内存泄漏
【发布时间】:2010-03-06 08:43:01
【问题描述】:

请帮我解决以下问题:

- (NSDictionary *)getGamesList
{
    NSMutableDictionary *gamesDictionary = [[NSMutableDictionary dictionary] retain];
// I was trying to change this on the commented code below, but did have no effect
//    NSMutableDictionary *gamesDictionary = [[NSMutableDictionary alloc] init];
//    [gamesDictionary retain];
    while (sqlite3_step(statement) == SQLITE_ROW)
    {
        NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
        NSArray *gameDate = [key componentsSeparatedByString:@" "];
        NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
        NSString *date_time = [NSString stringWithFormat:@"%@, %@",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
        if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
        [[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
        [_id release];
    }
    sqlite3_reset(statement);
    return gamesDictionary;
}

泄漏开始于另一个类的另一个方法,调用 getGamesList 方法,如下所示:

NSMutableDictionary *gamesDictionary;
gamesDictionary = [[NSMutableDictionary dictionaryWithDictionary:[appDelegate getGamesList]] retain];

之后有很多泄漏指向字符串中的 NSCFArray:

NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain]; 

在这个方法中:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
    if ([keys count] != 0)    return [[keys objectAtIndex:section] uppercaseString];
    return @"";
}

我假设这些东西是相互关联的,但我仍然无法理解所有的内存管理技巧。 非常感谢!

【问题讨论】:

    标签: cocoa memory nsmutabledictionary memory-leaks


    【解决方案1】:

    多年没有使用 Cocoa(这就是为什么我不能告诉你一个确切的答案:/)。但我猜你的问题是你系统地在你的对象上使用retain

    由于对象引用计数永远不会为 0,所有字典都保存在内存中而不是被释放。

    尝试删除[NSArray arrayWithArray][NSMutableDictionary dictionaryWithDictionary上的retain

    http://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_beginners/Some_Cocoa_essential_principles#Retain_and_Release

    【讨论】:

      【解决方案2】:

      看起来您确实过度保留了数组。

      当您创建 gamesDictionary 时,它的保留计数为 +1。然后你保留它(计数变为+2)。当您获得此函数之外的值时,您会再次保留(计数变为 +3)。

      你是正确的,如果你创建一个对象,你就负责它的内存管理。此外,当您从方法中获取对象时,如果您想保留它的时间超过函数的跨度,则应该保留它。在您的情况下,您只想获取对象的某些属性,因此您不需要保留它。

      这是一个建议:

      - (NSDictionary *)getGamesList
      {
          NSMutableDictionary *gamesDictionary = [NSMutableDictionary dictionary]; // Remove the retain.
          while (sqlite3_step(statement) == SQLITE_ROW)
          {
              NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
              NSArray *gameDate = [key componentsSeparatedByString:@" "];
              NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
              NSString *date_time = [NSString stringWithFormat:@"%@, %@",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
              if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
              [[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
              [_id release];
          }
          sqlite3_reset(statement);
          return gamesDictionary; 
      }
      

      接下来的部分很混乱。您创建一个新字典并保留它。原始字典不是自动释放的,因此计数不会减少并且它总是挂起。只需分配字典而不是创建新字典。

      NSMutableDictionary *gamesDictionary = [[appDelegate getGamesList] retain];
      // Retaining it, becuase it looks like it's used elsewhere.
      

      现在,在这个方法中:

      - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
      {
          NSString *returnString;
          // Don't need to retain the keys because you are only using it within the function
          // and since you didn't alloc, copy or retain the array it contains, you aren't responsible for it's memory management.
          NSArray *keys = [NSArray arrayWithArray:[gamesDictionary allKeys]];
          if ([keys count] != 0) {
              returnString = [[NSString alloc] initWithString:[[keys objectAtIndex:section] uppercaseString]];
              return [returnString autorelease];
          }
          return @"";
      }
      

      【讨论】:

      • 是的!它解决了问题!非常感谢,你让我很开心 :) 一切都是正确的,除了 return [gamesDictionary autorelease];在此构造中会导致 EXC_BAD_ACCESS 错误。只是使用了 return gamesDictionary ,一切都变得清晰了!再次感谢!
      • 是的,我的错。你没有“创建”游戏字典,所以你不负责它的内存管理,所以你不需要调用autorelease它。
      • 似乎只是冰山一角。我打开一个调用 getGamesList 的新视图(内存泄漏说没有错误),然后我返回父视图并得到很多泄漏。它们是: NSCFArray in if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];和 NSString 中的 NSCFString *date_time = [NSString stringWithFormat:@"%@, %@",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];和 NSNumber 中的 NSCFNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)]; :)
      • 也是 NSCFDictionary,但我通过添加 return [[gamesDictionary copy] autorelease]; 解决了这个问题
      • 问题出在 viewWillDisappear 方法中。这是 [self viewWillDisappear] 和 [gamesDictionary release] 的错误顺序 :) 再次感谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-17
      • 2013-04-18
      • 1970-01-01
      • 1970-01-01
      • 2013-12-05
      相关资源
      最近更新 更多