【问题标题】:Memory leaking with [NSKeyedUnarchiver decodeObjectForKey][NSKeyedUnarchiver decodeObjectForKey] 导致内存泄漏
【发布时间】:2010-09-15 04:54:41
【问题描述】:

每次调用此方法时,我的 NSMutableData 都会泄漏,我无法弄清楚如何插入它。在分配和初始化解码器后,theData 的保留计数增加了 1,我不知道为什么。我在方法结束时被保留计数为 2 并试图释放它会导致应用程序崩溃。

- (void)readVenueArchiveFile:(NSString *)inFile key:(NSString *)inKey
{
    NSMutableData *theData;
    NSKeyedUnarchiver *decoder;


    theData = [NSData dataWithContentsOfFile:inFile];

    decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData];

    venueIOList = [[decoder decodeObjectForKey:inKey] mutableCopy];

    [decoder finishDecoding];

    [decoder release];
}

【问题讨论】:

    标签: objective-c cocoa


    【解决方案1】:

    减少峰值内存占用

    一般来说,最好的做法是避免生成自动释放的对象。

    [本段大部分内容从this question 修改。] 由于您通常(1) 无法直接控制其生命周期,因此自动释放的对象可能会持续较长时间并不必要地增加应用程序的内存占用。虽然在桌面上这可能影响不大,但在更受限制的平台上,这可能是一个重大问题。因此,在所有平台上,尤其是在更受限制的平台上,强烈建议您不要使用会导致自动释放对象的方法,而是鼓励您使用 alloc/init 模式。

    我建议更换这个:

    theData = [NSData dataWithContentsOfFile:inFile];
    

    与:

    theData = [[NSData alloc] initWithContentsOfFile:inFile];
    

    然后在方法末尾添加:

    [theData release];
    

    这意味着theData 将在方法退出之前被释放。 你应该最终得到:

    - (void)readVenueArchiveFile:(NSString *)inFile key:(NSString *)inKey
    {
        NSMutableData *theData;
        NSKeyedUnarchiver *decoder;
    
        theData = [[NSData alloc] initWithContentsOfFile:inFile];
        decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData];
        ListClassName *decodedList = [decoder decodeObjectForKey:inKey];
        self.venueIOList = decodedList;
        [decoder finishDecoding];
        [decoder release];
        [theData release];
    

    }

    这使得内存管理语义清晰,并尽快回收内存。

    (1) 您可以使用自己的本地自动释放池进行控制。有关这方面的更多信息,请参阅Apple's Memory Management Programming Guide

    【讨论】:

      【解决方案2】:

      我建议替换这一行:

      venueIOList = [[decoder decodeObjectForKey:inKey] mutableCopy];
      

      与:

      ListClassName *decodedList = [decoder decodeObjectForKey:inKey];
      self.venueIOList = decodedList;
      

      这使得decodedList的内存管理变得清晰。使用访问器方法(初始化方法除外)分配实例变量被认为是最佳实践。在您当前的实现中,如果您曾在同一个对象上再次调用 readVenueArchiveFile:,您 泄漏(就像 decodedList 已经有值时一样)。此外,您可以将复制逻辑放在访问器方法中并忘记它,而不必在每次分配新值时都记住 mutableCopy(假设无论如何都有充分的理由制作可变副本?)。

      【讨论】:

        【解决方案3】:

        不要担心保留计数,担心方法内的平衡。假设 venueIOList 是一个实例变量,您在此方法中所做的看起来是正确的。

        稍微扩展我的答案:取消归档程序可能会在取消归档操作期间保留您的数据,然后在完成时发送数据-autorelease 而不是-release。因为那不是做的事,所以不必关心。

        【讨论】:

          【解决方案4】:

          引用计数相关的内存管理启示的最终来源仍然是,IMO,"Hold Me, Use Me, Free Me" from Stepwise。

          【讨论】:

            【解决方案5】:

            您的代码是正确的;没有内存泄漏。

            theData = [NSData dataWithContentsOfFile:inFile];

            等价于

            theData = [[[NSData alloc] initWithContentsOfFile:inFile] autorelease];

            此时 theData 的引用计数为 1(如果小于,它将被释放)。引用计数将在未来某个时候由自动释放池自动递减。

            decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData];

            解码器对象保留对 theData 的引用,这会将其引用计数增加到 2。

            方法返回后,自动释放池将这个值递减为1。如果你在这个方法结束时释放数据,引用计数将变为0,对象将被释放,当你尝试释放时你的应用会崩溃使用它。

            【讨论】:

            • 这令人困惑。 1. 在呈现的代码中,无法在方法之外引用 theData。 2.自动释放池在方法返回后不会减少保留计数,因此如果您可以在方法外部引用theData,则只有在池已耗尽时才会崩溃。
            • 您的分析不正确。 1. theData 被传递给保留它的解码器对象。 2. 是的,自动释放池在方法返回后确实会减少引用计数。在你给别人不正确的建议之前,先了解一下 Cocoa。
            • 不清楚为什么 (1) 与该数据是否可以在方法之外访问有关。
            • (2) 问题不在于自动释放池是否减少引用计数,而是何时 -- 方法返回后不这样做,但(通常)在事件循环的末尾。因此,如果您能够在方法之外访问 theData,则可能不会崩溃。
            • 想想你在说什么!如果自动释放的对象可以在声明它的方法的执行过程中的任何时候被释放,那么它将无法使用,因为您永远不会知道它是否已经被释放。想想吧!
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-12
            • 2018-05-19
            • 2015-07-06
            • 2014-06-07
            • 2013-11-20
            • 2011-10-28
            相关资源
            最近更新 更多