views:

719

answers:

5

When an instance method returns a value that was initialized with a convenience constructor, do I need to retain that object and then autorelease in the return so that when the convenience constructor's autorelease occurs, it doesn't remove the object.

Will this release description before the calling code and take ownership with a retain or something?

- (NSStringMutable *)test {
    NSMutableString *description = [NSMutableString stringWithString:@"Test Value"];
    return description;
}

Or should it be like this?

- (NSStringMutable *)test {
    NSMutableString *description = [NSMutableString stringWithString:@"Test Value"];
    [description retain];          
    return [description autorelease];
}

Calling Code:

NSMutableString *testVar = [[NSMutableString alloc] initWithString:[object description]];
A: 

No, you can simply return the autoreleased value. The reason for this is that autorelease isn't a function of the variable itself, it's a function of the autorelease pool, which (unless you create one yourself) is usually managed by the run loop.

Marc Charbonneau
+6  A: 

No, you should be fine with:

- (NSStringMutable *)test
{
    return [NSMutableString stringWithString:@"Test Value"];
}

This will leave the the object with a retain count of 1 and will be in the autorelease pool.

The autorelease pool is drained at specific times - it's not like a garbage collector. If you're implementing an event handler (like a button clicked handler), the autorelease pool is drained by the framework when you return from your event-handling code.

If you had used this:

- (NSStringMutable *)test
{
    NSMutableString *description = [NSMutableString stringWithString:@"Test Value"];
    [description retain];                                                               
    return [description autorelease];
}

...then the object would have a retain count of 2 and would be in the autorelease pool twice, and would actually behave in the same way as in the previous code example.

Daniel Yankowsky
+1 This answers the question
Abizern
You are correct, but try to avoid talking about retain counts, they are a red herring - there is no guarentee that NSMutableString will return an autoreleased object, only that it returns a mutable string that you do not currently own. You can safely return the result to your invoker simply because the memory managemnt rules explicitly say "That method may also safely return the object to its invoker". <http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html>
Peter N Lewis
That's a good point. I find thinking in terms of retain counts to be handy for explaining things. You're right - all you know is that stringWithString returns an object that is owned by somebody else. In this case, since NSMutableString actually allocated the object, it is NSMutableString's responsibility to release it.I was under the impression, though, that these convenience constructors (non-init constructors) conventionally return autoreleased objects. I would be surprised if I came across one that didn't behave this way.
Daniel Yankowsky
A: 

You are close with the second one, but you don't need the retain for the case you are talking about here and in fact don't need to call autorelease yourself.

This is valid, since stringWithString will return an autoreleased object already:

 - (NSStringMutable *)test
 {
   return [NSMutableString stringWithString:@"Test Value"];        
 }

In general when creating objects using Objective-C if you are calling a convenience constructor (not calling alloc and init) the return value is always autoreleased so stringWithString returns an autoreleased object the you can just return.

paulthenerd
+1  A: 

You can just return it. This is one of the main purposes of autorelease. Unless you've set up an autorelease pool of your own, the pool won't be drained until the next run through the event loop. The memory management programming guide explains all this in good detail — you should read that until you feel comfortable with it.

Side note: If this weren't safe and the autorelease pool were going to be drained early for some bizarre reason, giving it two retains and two autoreleases wouldn't make a difference. The numbers are still balanced, so it's still going to be released out of existence at some point.

Chuck
A: 

I've already voted on the correct answer, I'm adding this as a style note:

Your calling code isn't going to work, because it is calling [object description] when it should be calling [object test]

You don't need to be returning mutable strings unless you really want to be able to change the string. I personally try to minimise mutability in the code I write because I feel it is easier to maintain programs where state changes are minimal. You are only returning a description so I don't think it needs to be mutable. I know this is only some example code so maybe I'm being too picky

You could rewrite this as:

-(NSString *)description {
    // Just return a static NSString. No need to worry about memory management.
    return @"Test Value";
}

And if you want to be able to change the value of this returned string in your calling code:

NSMutableString *testVar = [[NSMutableString alloc]initWithString:[object description]];

Since you've called alloc on this string, you own it and are responsible for releasing it at some future date.

Alternatively, you can use one of my favourite bits of code:

NSMutableString *testVar = [[object description] mutableCopy];

This will return a mutable copy of even an immutable object (if it conforms to the NSMutableCopying protocol, of course). And you need to send [testVar release] at some stage.

And to tie this in as an actual answer to your question: if you send alloc, copy, mutableCopy, or retain to an object, then you own the object and are responsible for sending it a release message. You can assume that anything else returns an autoreleased object.

Again, I know this is only a quick bit of sample code that you asked a question on, but if you follow the above rule you've got most of your memory management problems sorted. In your first example you sent none of these for messages, so you don't need to release any memory yourself. However, you have an alloc in your calling code, so you own testVar and you need to release it.

Abizern
Thanks for the input. I did not know about mutableCopy. You are correct that this is just an example for purpose of my question. Thanks for the elaboration. I'm fairly new to objective c, and I hadn't even though of returning an NSString that I can pass to an initWithString for a NSMutableString. Thanks for the info.
Brian