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 *expressionDescripti开发者_运维问答on;
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. However, I have difficulty understanding how a program like Instruments can "know" that memory is being leaked. 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)
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).
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
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.
Name your message allocSOMETHING or newSOMETHING and don't call 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).
If you do either of these things, Build and Analyze will understand and help you get it right.
EDIT: added newSOMETHING based on comment
For the first part of your question, the answer is based on when autorelease actually releases the object.
- 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). 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.
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. For instance, it doesn't know that -[NSTimer invalidate]
releases the receiver, because that's not part of the common API conventions.
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). It comes with a great big dose of observer effect, though.
精彩评论