简体   繁体   English

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

[英]Two Objective-C Memory Management Questions: Is Leak Detection Always Right? and Why does autorelease work but not release?

I just discovered the following: As I expected, releasing my object before I return it causes the app to crash: 我刚刚发现了以下内容:正如我所料,在我返回它之前释放我的对象会导致应用程序崩溃:

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

    [expressionDescription release];
    return expressionDescription;
}

However, I did not expect that the following would cause a memory leak: 但是,我没想到以下会导致内存泄漏:

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

    return expressionDescription;
    [expressionDescription release];
}

Ultimately, my solution was to do this, instead: 最终,我的解决方案是这样做,而是:

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

    [expressionDescription autorelease];
    return expressionDescription;
}

I understand why autoreleasing it works, but how is a leak being caused by releasing after the value is returned? 我理解为什么自动释放它有效,但是如何在返回值后释放导致泄漏?

My second question is very related: Are memory leak detection systems always right? 我的第二个问题非常相关:内存泄漏检测系统是否总是正确的?

I realize that the programmers who developed Instruments and the Build and Analyze feature of XCode are far more experienced on this matter than I am, so for now I will assume that they are always right. 我意识到开发了仪器和XCode的构建和分析功能的程序员在这方面比我更有经验,所以现在我将假设他们总是正确的。 However, I have difficulty understanding how a program like Instruments can "know" that memory is being leaked. 但是,我很难理解像Instruments这样的程序如何“知道”内存被泄露。 I think it should depend entirely on how long I, the programmer, want to use an object. 我认为它应该完全取决于我,程序员,我想要使用一个对象多久。

This is my understanding of what a "leak" is: 这是我对“泄漏”的理解:

Human definition: Memory is being leaked if I have memory allocated when I am not using it. 人类定义:如果我在不使用内存时分配了内存,则内存正在泄露。

Programming Definition Using Counts: Memory is being leaked when memory is allocated but there are no active objects with a retain count on the object in question. 使用计数的编程定义:在分配内存时,内存正在泄漏,但是没有活动对象对相关对象有保留计数。

Programming Definition Using Accessibility: Memory is being leaked when there is allocated memory that can not be reached from wherever I am in the program right now. 编程定义使用辅助功能:当分配的内存无法从程序中的任何位置到达时,内存正在泄漏。

The problem with your second block is that no code is run after return. 第二个块的问题是返回后没有代码运行。 I would have expected Xcode to warn you about that (look at and try to fix your warnings, as well as errors) 我本来希望Xcode警告你(看看并尝试修复你的警告,以及错误)

Your understanding of leaks is correct. 您对泄漏的理解是正确的。 Build and Analyze can be fooled -- it relies on coding conventions being followed. 构建和分析可能被愚弄 - 它依赖于遵循的编码约定。 If you stray from that, B&A won't know (or will flag leaks that aren't true). 如果你偏离了这一点,B&A将不会知道(或者会标记不正确的泄漏)。

The Leak detector instrument inserts code into your program to check your Accessibility definition. 泄漏检测仪器会在程序中插入代码以检查您的辅助功能定义。 It can possibly be fooled by casting, but if you just do fairly straightforward allocations, assignments, and releases, I would take everything it flags very seriously unless you are absolutely sure it is wrong. 它可能会被强制转换所愚弄,但如果你只是做相当简单的分配,分配和发布,我会非常认真地对待它所有的标志,除非你绝对确定它是错误的。

When you return an object that you allocate, do one of the following 返回分配的对象时,请执行以下操作之一

  1. Call autorelease on it before returning it -- in this case the caller is responsible for retaining it if it wants it longer. 在返回它之前调用它自动释放 - 在这种情况下,调用者负责保留它,如果它想要更长时间。 An autoreleased object is released after the entire callstack is unwound back to the iOS call that called you -- that's where the "pool is drained" -- you should retain it before returning back to iOS. 在整个callstack退回到调用你的iOS调用之后释放一个自动释放的对象 - 这就是“池被耗尽”的地方 - 你应该在返回iOS之前保留它。

  2. Name your message allocSOMETHING or newSOMETHING and don't call autorelease. 将您的消息命名为allocSOMETHING或newSOMETHING,不要调用autorelease。 In this case, your message is understood to be returning an object with a retain count of one and the caller is responsible for releasing it (or autoreleasing it). 在这种情况下,您的消息被理解为返回一个保留计数为1的对象,并且调用者负责释放它(或自动释放它)。

If you do either of these things, Build and Analyze will understand and help you get it right. 如果您执行上述任一操作,Build and Analyze将了解并帮助您做到正确。

EDIT: added newSOMETHING based on comment 编辑:根据评论添加newSOMETHING

For the first part of your question, the answer is based on when autorelease actually releases the object. 对于问题的第一部分,答案是基于autorelease实际释放对象的时间。

  • The first example releases the object before it gets returned and used. 第一个示例在返回和使用之前释放对象。 You get a crash because the memory no longer exists. 因为内存不再存在而导致崩溃。
  • The second example never releases the memory, return causes control to leave the method. 第二个例子从不释放内存,返回导致控件离开方法。 Therefore your release call is in a 'no-mans land' of code, it never ever gets executed. 因此,您的发布调用是在代码的“无人区域”,它永远不会被执行。
  • Your final example works because autorelease adds the object to a list of objects to be released (called the autorelease pool). 您的最后一个示例有效,因为autorelease将对象添加到要释放的对象列表(称为自动释放池)。 When the pool is drained, all of the objects in it are sent a release message. 当池耗尽时,其中的所有对象都会发送一条释放消息。 The standard pool is drained when the event loop ticks over, essentially after your methods have handed control back to the OS. 当事件循环结束时,标准池将耗尽,主要是在您的方法将控制权交还给操作系统之后。 Because of this it is around while you want to use it, but is removed automatically in the long term. 因此,当你想要使用它时它就在附近,但是从长远来看会被自动删除。

The second part of your question has a kind of varying answer. 你问题的第二部分有一个不同的答案。 As far as I know, leak detection systems are not always right, because at least part of them is based on a heuristic. 据我所知,泄漏检测系统并不总是正确的,因为它们中的至少一部分是基于启发式的。 However, the leak detection in Instruments errs on the side of caution, so you can be 99% sure that if it reports a leak, then you do have a leak. 但是,仪器中的泄漏检测是谨慎的,因此您可以99%确定如果它报告泄漏,那么您确实有泄漏。

The way that they work is essentially to keep track of what objects are actually being pointed to. 它们的工作方式主要是跟踪实际指向的对象。 If it finds an object that has nothing pointing to it, but has not been freed, then it is classed as a leak. 如果它找到一个没有任何指向它但没有被释放的对象,那么它被归类为泄漏。

Are memory leak detection systems always right? 内存泄漏检测系统总是正确吗?

No. I've given a similar answer to a different question: the Leaks instrument is very conservative. 不,我对一个不同的问题给出了类似的答案:泄漏工具非常保守。 Anything that it reports as a leak really is a leak, but it doesn't necessarily report all leaks. 它报告为泄漏的任何东西确实是泄漏,但它不一定报告所有泄漏。 The static analyser relies on knowing rules about how the Cocoa/Cocoa Touch APIs work, so it can't always get it right either. 静态分析器依赖于了解有关Cocoa / Cocoa Touch API如何工作的规则,因此它也无法始终正确运行。 For instance, it doesn't know that -[NSTimer invalidate] releases the receiver, because that's not part of the common API conventions. 例如,它不知道-[NSTimer invalidate]释放接收器,因为这不是通用API约定的一部分。

The Zombies instrument (you can't, AFAICT, use it with iOS apps) on the other hand is logically perfect: any attempt to access a zombie object would be noticed (it would have identified the problem you've expressed above). 另一方面,Zombies乐器(你不能,AFAICT,与iOS应用程序一起使用)在逻辑上是完美的:任何访问僵尸对象的尝试都会被注意到(它会识别出你上面已经表达过的问题)。 It comes with a great big dose of observer effect, though. 然而,它具有很大的观察效果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM