【问题标题】:Releasing NSArray containing NSDictionary objects释放包含 NSDictionary 对象的 NSArray
【发布时间】:2011-01-08 12:18:22
【问题描述】:

我在 iPhone SDK 3.1 上的以下代码段中难以理解内存管理。

// Create array to hold each PersonClass object created below
NSMutableArray *arrayToReturn = [[[NSMutableArray alloc] init] autorelease];

NSArray *arrayOfDictionaries = [self generateDictionaryOfPeople];

[arrayOfDictionaries retain];

for (NSDictionary *dictionary in arrayOfDictionaries) {

    PersonClass *aPerson = [[PersonClass alloc] init];

    for (NSString *key in [dictionary keyEnumerator]) {

        if ([key isEqualToString:[[NSString alloc] initWithString: @"FIRST_NAME"]])
            aPerson.firstName = [dictionary objectForKey:key];

        else if ([key isEqualToString:[[NSString alloc] initWithString: @"LAST_NAME"]])
            aPerson.lastName = [dictionary objectForKey:key];

    }

    // Add the PersonClass object to the arrayToReturn array
    [arrayToReturn addObject: aPerson];

    // Release the PersonClass object
    [aPerson release];

}

return arrayToReturn;

[self generateDictionaryOfPeople] 方法返回一个 NSDictionary 对象数组。每个 NSDictionary 对象都有两个键“FIRST_NAME”和“LAST_NAME”,其中一个人的名字和姓氏作为各自的数据。代码循环遍历 arrayOfDictionaries 数组中的每个字典对象,并将字典数据分配给 aPerson (PersonClass) 对象的相关属性。然后将该对象添加到从该方法返回的数组中。

在运行仪器时,我发现 arrayOfDictionaries 数组中包含的字典对象泄漏。 [self generateDictionaryOfPeople] 方法中的代码在每个 NSDictionary 对象被创建并添加到数组时调用[dictionaryObject release],这使得对象 1 的保留计数(因为将对象添加到数组将使保留计数 2 ,但随后我的发布消息将其减回到 1)。

我假设这个泄漏是因为我从不释放 arrayOfDictionaries 数组,因此数组中的 NSDictionary 对象永远不会被释放。如果我尝试在上述代码段的末尾释放数组,则会收到“发送到已释放实例的消息”错误。我理解为什么会发生这种情况,因为我在一个字典项(我随后发布)中分配了 aPerson 对象数据,但我不知道在哪里可以释放 arrayOfDictionaries 数组。任何帮助将不胜感激。

谢谢!

编辑:下面是[self generateDictionaryOfPeople]的实现

- (NSArray *)generateDictionaryOfPeople {

    NSMutableArray *arrayFromDatabase = [[[NSMutableArray alloc] init] autorelease];

    // ** Query the database for data **

    while ( there are rows being returned from the database ) {

        // Declare an NSMutableDictionary object
        NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];

        // Loop through each column for that row
        for ( while there are columns for this row ) {

            columnTitle = title_of_column_from_database
            columnData = data_in_that_column_from_database

            // Add to the dictionary object
            [dictionary setObject:columnData forKey:columnTitle];

            // Release objects
            [columnName release];
            [columnTitle release];

        }

        // Add the NSMutableDictionary object to the array
        [arrayFromDatabase addObject:dictionary];

        // Release objects
        [dictionary release];

    }

    // Return the array
    return arrayFromDatabase;

}

【问题讨论】:

  • 请看在 OOP 的份上,创建一个 Person 类,而不是用字典来搞乱。即使我们只是一个字典的包装器。
  • @Mike:不幸的是我不能这样做,因为该类实际上从数据库中获取其他对象的数据,而不仅仅是 Person 对象的数据。上面的代码是代码的简化示例(因为我不想通过将所有数据库查询代码也放入其中来混淆情况)。

标签: iphone objective-c cocoa memory-management memory-leaks


【解决方案1】:

你没有释放arrayFromDatabase。 (避免这种错误的最简单方法是尽早使用工厂和autorelease,而不是手动延迟发布。在这种情况下,请使用[NSMutableDictionary dictionary]而不是[[NSMutableDictionary alloc] init]。)

【讨论】:

  • @Ahruman:感谢您的回复。这实际上是我的代码中的一个小错字 - 我现在已经编辑了代码,您将在 generateDictionaryOfPeople 的倒数第二行看到我正在发布 arrayFromDatabase。我也尝试了您的建议,但在表格视图控制器显示消息“[CFString release]:消息发送到已释放实例”后,我仍然立即崩溃。
【解决方案2】:

由于没有释放 arrayOfDictionaries 数组,我仍然得到原始泄漏。

这意味着您忘记在 generateDictionaryOfPeople 中自动释放它。

您需要查看the memory management rules

【讨论】:

  • @Peter:我已经在 generateDictionaryPeople 方法中自动释放了返回的数组,尽管我可能做错了。我现在更新了问题以包含该方法。
  • 好的,所以你正在自动释放那个。但是,您不会释放放入 PersonClass 对象的数组。您应该按照相同的规则自动释放它们。
  • @Peter:感谢您的回复。我目前已经按原样更新了上面的代码。但是,如果我使用 NSMutableArray *arrayToReturn = [[[NSMutableArray alloc] init] autorelease] 自动释放放置 PersonClass 对象的数组,那么程序将简要显示 UITableViewController 以及该数组中的相关数据,然后立即因“发送到已释放实例的消息”而崩溃。目前这是一个非常基本的程序,我没有对该数组做任何其他花哨的事情,所以我不明白崩溃?
  • 任何持有数组的东西都需要拥有它,只要它拥有它,它就应该保留它。再次阅读内存管理规则。
  • @Peter:感谢您的回复,但是在阅读了有关内存管理的规则和其他指南后,我仍然遇到问题。我已经用最新的代码更新了代码。我添加了各种 [autorelease] 语句并且代码有效,但我仍然遇到各种泄漏。我认为这是由于 arrayOfDictionaries 从未被释放。但是,因为对象是从 arrayOfDictionaries 中包含的字典中取出的,并分配给其他地方使用的其他变量,所以我不知道我在哪里释放这个对象? arrayOfDictionaries 上的 Autorelease 也会崩溃。
【解决方案3】:

这里,

    if ([key isEqualToString:[[NSString alloc] initWithString: @"FIRST_NAME"]])
        aPerson.firstName = [dictionary objectForKey:key];

    else if ([key isEqualToString:[[NSString alloc] initWithString: @"LAST_NAME"]])
        aPerson.lastName = [dictionary objectForKey:key];

替换为

    if ([key isEqualToString:@"FIRST_NAME"])
        aPerson.firstName = [dictionary objectForKey:key];

    else if ([key isEqualToString:@"LAST_NAME"])
        aPerson.lastName = [dictionary objectForKey:key];

泄漏的问题是您在没有 -release-ing 的情况下每个循环创建 1 ~ 2 个 NSString-s。如果需要常量NSString-s,直接使用即可。

【讨论】:

  • 感谢您的回复,但由于未释放 arrayOfDictionaries 数组,我仍然得到原始泄漏。关于如何以及何时释放此对象的任何想法?
  • @Mark:你需要说明-generateDictionaryOfPeople是如何定义的。顺便说一句,arrayToReturn 应该是-autorelease-ed。
  • @KennyTM:感谢您的回复。我试过把arrayToReturn = [[[NSMutableArray alloc] init] autorelease]。该程序加载并显示表视图控制器(带有正确的数据)不到一秒钟,然后在控制台上出现“发送到已释放实例的消息”消息而崩溃。我已经用 -generateDictionaryOfPeople 的实现更新了我的问题。
  • @Mark:你需要在-generateDictionaryOfPeople-autorelease 你的arrayFromDatabase。您的程序崩溃意味着您在方法的返回值上有一个错误的-release。你不应该-release 一个返回的对象除非它被命名为create……copy…init…。请查看您的计划的所有权政策。
  • @KennyTM:感谢您的回复肯尼。我想您可能已经阅读了上面的更新代码,然后我才对方法进行了另一个更新。正如您现在将看到的,我需要将 NSMutableArray 转换为要返回的 NSArray,在此过程中,我向返回的 NSArray 添加了自动释放并手动释放了 NSMutableArray(不再需要)。我还是不太明白我做错了什么?
猜你喜欢
  • 2011-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-16
  • 1970-01-01
  • 1970-01-01
  • 2012-11-07
相关资源
最近更新 更多