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. 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