两个Objective-C内存管理问题:泄漏检测是否始终正确?为什么autorelease工作但不释放?

时间:2010-12-24 15:42:49

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

我刚发现以下内容: 正如我所料,在我返回之前释放我的对象会导致应用程序崩溃:

   + (NSString *)descriptionOfExpression:(NSArray *)anExpression {
    NSMutableString *expressionDescription;
    expressionDescription = [[NSMutableString alloc] init];  
    for (id object in anExpression) {
    //Do stuff to expressionDescription
    }

    [expressionDescription release];
    return expressionDescription;
}

但是,我没想到以下情况会导致内存泄漏:

    + (NSString *)descriptionOfExpression:(NSArray *)anExpression {
    NSMutableString *expressionDescription;
    expressionDescription = [[NSMutableString alloc] init];  
    for (id object in anExpression) {
    //Do stuff to expressionDescription
    }

    return expressionDescription;
    [expressionDescription release];
}

最终,我的解决方案就是这样做,而不是:

    + (NSString *)descriptionOfExpression:(NSArray *)anExpression {
    NSMutableString *expressionDescription;
    expressionDescription = [[NSMutableString alloc] init];  
    for (id object in anExpression) {
    //Do stuff to expressionDescription
    }

    [expressionDescription autorelease];
    return expressionDescription;
}

我理解为什么自动释放它有效,但是如何在返回值后释放导致泄漏?

我的第二个问题非常相关:内存泄漏检测系统是否总是正确的?

我意识到开发了仪器和XCode的构建和分析功能的程序员在这方面比我更有经验,所以现在我将假设他们总是正确的。但是,我很难理解像Instruments这样的程序如何“知道”内存被泄露。我认为它应该完全取决于我,程序员,我想要使用一个对象多久。

这是我对“泄密”的理解:

人类定义:如果我在不使用内存时分配了内存,则会泄露内存。

使用计数的编程定义:在分配内存时,内存正在泄漏,但是没有活动对象对相关对象有保留计数。

编程定义使用辅助功能:当存在无法从程序中的任何位置到达的分配内存时,内存正在泄露。

3 个答案:

答案 0 :(得分:5)

第二个块的问题是返回后没有代码运行。我原以为Xcode会警告你(看看并尝试修复你的警告,以及错误)

您对泄漏的理解是正确的。构建和分析可能被愚弄 - 它依赖于遵循的编码约定。如果你偏离了这一点,B& A将不知道(或将标记泄漏不是真的)。​​

泄漏检测仪器会在程序中插入代码以检查您的辅助功能定义。它可能会被强制转换所愚弄,但是如果你只是进行相当简单的分配,分配和发布,我会非常认真地对待它的所有内容,除非你绝对确定它是错误的。

当您返回分配的对象时,请执行以下操作之一

  1. 在返回之前调用autorelease - 在这种情况下,调用者负责保留它,如果它想要更长时间。在整个callstack退回到调用你的iOS调用之后释放自动释放的对象 - 这就是“池被耗尽”的地方 - 你应该在返回iOS之前保留它。

  2. 将邮件命名为allocSOMETHING或newSOMETHING,不要调用autorelease。在这种情况下,您的消息被理解为返回一个保留计数为1的对象,并且调用者负责释放它(或自动释放它)。

  3. 如果您执行上述任一操作,Build and Analyze将理解并帮助您做到正确。

    编辑:根据评论添加newSOMETHING

答案 1 :(得分:2)

对于问题的第一部分,答案是基于autorelease实际释放对象的时间。

  • 第一个示例在返回并使用之前释放该对象。因为内存不再存在而导致崩溃。
  • 第二个例子从不释放内存,返回导致控制离开方法。因此,您的发布调用是在代码的“无人区域”,它永远不会被执行。
  • 您的最终示例有效,因为autorelease将对象添加到要释放的对象列表(称为自动释放池)。当池耗尽时,其中的所有对象都会发送一条释放消息。当事件循环结束时,标准池将耗尽,主要是在您的方法将控制权交还给操作系统之后。因此,当你想要使用它时它就在身边,但是从长远来看会被自动删除。

你问题的第二部分有一个不同的答案。据我所知,泄漏检测系统并不总是正确的,因为它们中的至少一部分是基于启发式的。但是,仪器中的泄漏检测是谨慎的,因此您可以99%确定如果它报告泄漏,那么您确实有泄漏。

它们的工作方式主要是跟踪实际指向的对象。如果它找到一个没有任何指向它但没有被释放的对象,则它被归类为泄漏。

答案 2 :(得分:1)

  

是内存泄漏检测系统   总是对的?

没有。我对一个不同的问题给出了类似的答案:泄漏工具非常保守。它报告为泄漏的任何东西确实是泄漏,但它不一定报告所有泄漏。静态分析器依赖于了解有关Cocoa / Cocoa Touch API如何工作的规则,因此它也无法始终正确运行。例如,它不知道-[NSTimer invalidate]释放接收者,因为这不是通用API约定的一部分。

另一方面,僵尸乐器(你不能,AFAICT,与iOS应用程序一起使用)在逻辑上是完美的:任何试图访问僵尸对象的行为都会被注意到(它会识别出你在上面表达过的问题) )。然而,它具有很大的观察效果。