donkim
donkim

Reputation: 13137

iPhone memory management (with specific examples/questions)

Hey all. I know this question's been asked but I still don't have a clear picture of memory management in Objective-C. I feel like I have a pretty good grasp of it, but I'd still like some correct answers for the following code. I have a series of examples that I'd love for someone(s) to clarify.

Setting a value for an instance variable:

Say I have an NSMutableArray variable. In my class, when I initialize it, do I need to call a retain on it?

Do I do

fooArray = [[[NSMutableArray alloc] init] retain];

or

fooArray = [[NSMutableArray alloc] init];

Does doing [[NSMutableArray alloc] init] already set the retain count to 1, so I wouldn't need to call retain on it? On the other hand, if I called a method that I know returns an autoreleased object, I would for sure have to call retain on it, right? Like so:

fooString = [[NSString stringWithFormat:@"%d items", someInt] retain];

Properties:

I ask about the retain because I'm a bit confused about how @property's automatic setter works.

If I had set fooArray to be a @property with retain set, Objective-C will automatically create the following setter, right?

- (void)setFooArray:(NSMutableArray *)anArray {
    [fooArray release];
    fooArray = [anArray retain];
}

So, if I had code like this: self.fooArray = [[NSMutableArray alloc] init]; (which I believe is valid code), Objective-C creates a setter method that calls retain on the value assigned to fooArray. In this case, will the retain count actually be 2?

Correct way of setting a value of a property:

I know there are questions on this and (possibly) debates, but which is the right way to set a @property?

This?

self.fooArray = [[NSMutableArray alloc] init];

Or this?

NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.fooArray = anArray;
[anArray release];

I'd love to get some clarification on these examples. Thanks!

Upvotes: 2

Views: 710

Answers (3)

Anurag
Anurag

Reputation: 141899

Ideally you would want to use the accessors whenever possible, especially when dealing with objects as they help avoid many memory issues. So even for instance variables, you should do:

self.fooArray = ...;

instead of

fooArray = ...;

The reason why you should declare properties for object instance variables is because the memory management is slightly more complicated, and recreating it by hand each time is tricky. The correct setter for a nonatomic, retained property would look like:

- (void)setFoo:(NSArray *)aFoo {
    if (foo == aFoo) {
        return;
    }
    NSArray *oldFoo = foo;
    foo = [aFoo retain];
    [oldFoo release];
}

You are right about the instance variable having a retain count of 2 when you do something like this (assuming foo is retained):

self.foo = [[NSMutableArray alloc] init];

The first retain count is coming from alloc, and the second one from your synthesized setter. Any of these should work:

// longer, explicit version, releases immediately (more efficient)
NSMutableArray *aFoo = [[NSMutableArray alloc] init];
self.foo = aFoo;
[aFoo release];

// autoreleased, not so bad unless you're a memory management freak
self.foo = [[[NSMutableArray alloc] init] autorelease];

// an even shorter version of the above
self.foo = [NSMutableArray array];

To create private properties, you can declare them as a class extension in the .m implementation file. To give an example, consider a simple Person object, which has a name, and a boolean property didSave which simply indicates whether the object has been saved to some database or not. Since we don't want to expose this to the outside world, but still keep the benefits of properties inside the implementation file, we can create the header file will all instance variables (public, private, protected) and only public properties:

// Person.h

@interface Person {
    NSString *name;

    @private
    BOOL didSave;
}

@property (nonatomic, retain) NSString *name;

@end

But declare private properties inside the implementation:

// Person.m

// property is declared as a class extension, making it 
// invisible to the outside world.
@interface Person ()
    @property BOOL didSave;
@end

@implementation

// synthesize as normal
@synthesize name, didSave;

@end

Upvotes: 3

Jacob Relkin
Jacob Relkin

Reputation: 163288

According to Apple's Object Ownership Policy, any method that begins with the words alloc or new, or contains copy is owned by the caller.

To obtain ownership of an object, you must retain it.

So, in your first example, the retain is unnecessary because you already own the object.

The correct way to do this:

fooArray = [[NSMutableArray alloc] init];

Since autoreleased objects are owned by the current autorelease pool, you must call retain on them to gain ownership of them, so this example is correct:

fooString = [[NSString stringWithFormat:@"%d items", someInt] retain];

This would work fine as well:

self.fooString = [NSString stringWithFormat:@"%d items", someInt]; //retained by property setter

And for your last example using the property setter, this would be the correct way to do it:

NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.fooArray = anArray;
[anArray release];

Instead of having to do the above, I'd suggest the following solution:

self.fooArray = [NSMutableArray arrayWithCapacity:10];

arrayWithCapacity: will return an autoreleased NSMutableArray, which is the retain-ed by the property setter method. :)

Upvotes: 4

Di Wu
Di Wu

Reputation: 6448

First of all, with this line:

fooArray = [[NSMutableArray alloc] init];

fooArray will automatically have a retain count of 1.

Second, yes, it's 2. And your guess on the setter implementation is correct.

Third, the latter one is right

Upvotes: 0

Related Questions