tags:

views:

635

answers:

6

I'm slowly teaching myself cocoa for the iPhone(through the Stanford Class on iTunes U) and I've just gone through the part on memory management, and I wanted to hopefully get some confirmation that the assumptions I'm making on how memory is handled and how [release] and [autorelease] work. Since memory management is a really basic and fundamental, but very essential part of the programming experience, I'd like to make sure I'm doing it right.

I understand that anything with an alloc, new, or copy needs to be released.
If I do this:

NSString *temp = [[NSString alloc] initWithString:@"Hello World"];

Then I need to add [temp release/autorelease] eventually, since I have an alloc.

However, if I do this:

NSString *temp = @"Hello World";

Then it doesn't seem to need a release statement. Does the NSString class call autorelease automatically as part of the assignment?

Also, is there any difference between the two *temp objects here after these statements? They both contain the same string, but are there memory/usage ways where they differ?

Secondly, with properties, I'm assuming that the autorelease is handled automatically. If I have this:

@interface Person : NSObject
{
    //ivars
    NSString *firstName;
    NSString *lastName;
}

//properties
@property NSString *firstName;
@property NSString *lastName;

///next file

@implementation Person

@synthesize firstName;
@synthesize lastName;

- (void) dealloc
{

    //HERE!!!!

    [super dealloc];
}

I'm assuming I don't need to add [firstName release] and [lastName release] (at //HERE!!!!), since that's automatically handled by the properties. Is that correct?

I do understand that if I do this in code(assuming I've defined initWithFirstName):

Person *Me = [[Person alloc] initWithFirstName: @"Drew", lastName:"McGhie"];

that later I'm going to have to use [Me release/autorelease];

Any help confirming or correcting my understanding so far is greatly appreciated.

POST ANSWER WRITE-UP

I thought I'd write this all up after going over all the answers and testing out the suggestions and talk about what worked.

I do need to add the [firstName release], [lastName release], but I also need to add (retain) to the property descriptions. Not adding the (retain) caused warnings because it assumes (assign). Here's how I finally set up the class

@interface Person : NSObject
    {
        //ivars
        NSString *firstName;
        NSString *lastName;
    }

    //properties
    @property (retain) NSString *firstName;
    @property (retain) NSString *lastName;

    ///next file

    @implementation Person

    @synthesize firstName;
    @synthesize lastName;

    - (void) dealloc
    {
        [firstName release];
        [lastName release];
        [super dealloc];
    }
+1  A: 

Last part first: You will indeed have to auto/release Me. However, you will also have to add [firstName release]; and [lastName release]; in -dealloc; or better yet; self.firstName = nil;

As for string literals; this part gets a bit hairy, but [@"String literal" release] is essentially a noop. As such, there is a difference between the two temp objects, but as you won't generally know which one you'll be dealing with, adding [temp release]; is generally the safe choice, unless you know it'll contain an autoreleased object.

Williham Totland
I don't explicitly alloc/copy/retain firstName or lastName. Is it needed because the alloc is inferred by the synthesize auto-generation that I just don't see in the code?
Drew McGhie
In a sense. The issue at hand is that your object "owns" `firstName` and `lastName`, so when they are set, it becomes your responsibility to release them.
Williham Totland
+10  A: 

The rule is simple: if you alloc, copy or retain, it's your responsibility to release. If you didn't, it's not. However, if you need to rely on an object staying around, you have to retain (and subsequently release).

We can treat the string literal according to the rules - you don't need to release it because you don't own it. That's simple; there's no need to worry about whether they're special cases or not, just follow the rules and you'll be fine.

I wrote up a blog post with a collection of articles about the Cocoa memory management rules; I'd recommend following up some of the references.

Graham Lee
+3  A: 
  1. I've never released string constants like NSString *foo = @"x";. Logically, if you had to release the result of that, it you would have to release the parameter to initWithString, and both the parameters to initWithFirstName:lastName:, too.
  2. You must do release or autorelease firstName and lastName. I've seen warnings about not using property syntax in destructors, which I think is the same reason you don't use virtual functions in C++ constructors and destructors.

Your assumption was wrong. You must do either this:

    Person *Me = [[Person alloc] initWithFirstName: @"Drew"
                                          lastName: @"McGhie"];
    ...
    [Me release];

or this:

    Person *Me = [Person personWithFirstName: @"Drew"
                                    lastName: @"McGhie"];

...and make sure your Person object handles +personWithFirstName:lastName: correctly, i.e. [[[self alloc] initWithFirstName: firstName lastName: lastName] autorelease].

You should probably do the one with less code. Clarity is important, NSAutoreleasePool will probably never be your bottleneck, and if it ever is it's easily fixed.

I think people put a lot of effort into avoiding the class messages that return an autorelease'd object that just isn't merited. It's premature optimization, in that it probably isn't necessary and may not even be the correct thing to do. And it's harder to maintain, you'll very likely be chasing leaks forever.

Also, you're going to autorelease an object you had to init (i.e. alloc + initPersonWithFirstName:lastName: instead of using a class message like personWithFirstName:lastName:), I'd suggest you do it immediately. Otherwise, you're potentially chasing that same kind of leak. So if you're not going to add a personWithFirstName:lastName: method to Person, do this instead:

    Person *Me = [[[Person alloc] initWithFirstName: @"Drew"
                                           lastName: @"McGhie"] autorelease];

Summary: Cocoa does a lot to help you with memory management. Make sure you don't fight it.

Updated based on Jon's feedback in comment.

Steven Fisher
Rather than [[[Person alloc] initWithFirstName: firstName lastName: lastName] autorelease], [[[self alloc] initWithFirstName: firstName lastName: lastName] autorelease] would be more appropriate. By using self, you'll allocate the correct type of object is there is a person subclass. For example: [SpecialPerson personWithFirstName:@"Drew" lastName:@"McGhie"] would correctly instantiate a special person.In a "+" method, self refers to the class that owns the method.
Jon Hess
That's a *fantastic* tip, Jon. Will check and update answer. Thanks!
Steven Fisher
+1  A: 

About firstName/lastName.

You should always, for clarity, remember to specify the properties' attributes. The setter default attribute is assign, in this case you want to use retain.

@interface Person : NSObject
{
    NSString *firstName;
}
@property (retain) NSString *firstName;
@end

With retain each and only time you use the dot notation to assign a value the compiler inserts a retain: just remember to always use it. For consistency I advice you to write your initializer this way:

- (id) initWithFirstName:(NSString*) aString
{
    self.firstName = aString; 
}

and the dealloc method this way:

- (void) dealloc
{
     self.firstName = nil;
}

About @""-type objects. They are constant NSStrings objects. Just use them as the were (they are) NSString objects and never release them. The compiler takes care of them.

IlDan
You should not write init/dealloc this way. This approach will send a message to the object, which may be handled by a subclass instead of actually getting to your class.
Steven Fisher
I heard about Apple recommending against getter/setters in init/dealloc, but I can't find the docs about it. Could you post a link?
IlDan
+1  A: 

Does the NSString class call autorelease automatically as part of the assignment?

The NSString class didn't do anything because you didn't send it a message. All you did was assign to a variable. NSString doesn't find out about this, and it's none of its business.

Also, is there any difference between the two *temp objects here after these statements? They both contain the same string, but are there memory/usage ways where they differ?

They're both NSStrings, they both contain the same value, and they're both presumed immutable. That's all you should ever care about.

Secondly, with properties, I'm assuming that the autorelease is handled automatically. If I have this:

@property NSString *firstName;
@property NSString *lastName;

- (void) dealloc
{
    //HERE!!!!

    [super dealloc];
}

I'm assuming I don't need to add [firstName release] and [lastName release] (at //HERE!!!!), since that's automatically handled by the properties. Is that correct?

No. NSObject will not release all your property values for you; you still need to release them yourself there.

Also, don't do self.firstName = nil and self.lastName = nil in dealloc. Those translate into messages (to your accessor methods), and when you do that in dealloc, you're sending messages to a half-deallocked object. That's asking for trouble. The same applies the other way to initializing property values in init: Using your properties/accessors there would be sending messages to a half-inited object.

Peter Hosey
+2  A: 

NSStrings created with the @"String here" syntax are constant strings. These are different from normal strings. Much like normal C constant strings, they are created when your program loads and exist for its entire lifetime. The NXConstantString class, to which they belong, ignores all memory management messages. You can retain and release them all you like and it won't make any difference.

For a string created with an [[NSString alloc] initWith...]-type method, normal memory management rules apply. I'd strongly recommend reading the linked docs — they're not complicated, and after reading them, you will know pretty much everything you will ever need to know about Cocoa memory management.

Chuck