简体   繁体   中英

differing methods of alloc / init / retaining an object in objective-c

In several pieces of sample objective-c code I've seen people create new objects like this:


RootViewController *viewController = [[RootViewController alloc] init];
self.rootViewController = viewController; // self.rootViewController is a (nonatomic,retain) synthesized property
[viewController release];
[window addSubview: [self.rootViewController view]];

Is that any different "behind the scenes" than doing it like this instead?


self.rootViewController = [[RootViewController alloc] init];
[window addSubview: [self.rootViewController view]];

Edit: later I release rootViewController in my dealloc method:


-(void) dealloc {
    [rootViewController release];
    [super dealloc];
}

I'm just curious about the syntax between the two. One seems to create a temporary viewController object, and the other allocs/inits directly to self.rootViewController.

Seems a bit more straightforward/streamlined that way so I'm wondering why anyone would opt for the first method.

Thanks!

Answer: So it looks like when using the second method it causes a memory leak because the rootViewController object would actually have a retain count of 2. (See my answer below with a link to a post that thoroughly explains it.) Thanks everyone!

This line:

self.rootViewController = viewController

is identical to this line:

[self setRootViewController:viewController];

The typical setX call will release a previously retained value of X and assign a new retained value of X.

id old = X;
X = [new retain];
[old release];

But it can do anything else as well.

If you know there is not a current value to release (in init) and the setter function does nothing but retain the new value (synthesized), you can replace:

RootViewController *viewController = [[RootViewController alloc] init];
self.rootViewController = viewController; // self.rootViewController is a (nonatomic,retain) synthesized property
[viewController release];

With:

rootViewController = [[RootViewController alloc] init];

which does not use self. and so directly assigns a value instead of calling a setter method. Using the setter method is generally preferred.

To consolidate the lines as you want, you can also switch to this:

self.rootViewController = [[[RootViewController alloc] init] autorelease];

Which uses the setter method, releases the allocated instance, and fits on one line.

Your second code snipppet doesn't release the object you created.

self.rootViewController is a property that retains the object. So you're creating an object using alloc, and then the setter method for self.rootViewController will retain it also. You should release all objects that you allocated. Always

What happens is:

  1. You create an object of type RootViewController using alloc , so the retain count becomes 1
  2. The object is assigned to a property which also retains the object. So the retain count becomes 2

When self is deallocated later on, the retained RootViewController object will be released, so its retain count becomes 1 again.

Result: you have a memory leak.

In the second code snippet there will be a memory leak because you omit the [self.rootViewController release] line.

In more detail:

  • When you call [[RootViewController alloc] init] , the retain count of the created object will be 1.
  • Calling self.rootViewController = viewController will increase it to 2 because the self.rootViewController property is retaining.
  • Calling [viewController release] decreases the retain count to 1
  • So if you call self.rootViewController = nil later, then the retain count will be 0 (because the generated setter calls a release method), so the object will be deallocated.

In the second case, the retain count will be 1 when you call self.rootViewController = nil , so the object will never be released.

If you want a more compact solution, try this:

self.rootViewController = [[[RootViewController alloc] init] autorelease];
[window addSubview: [self.rootViewController view]];

Please read and understand the Cocoa Memory MAnagement Rules . You obtained the object with alloc, therefore you have ownership of it. By "you" I mean the executing code in the current scope. You need to either release or autorelease it to relinquish ownership. Assigning the object to something else (in this case a property) does not absolve you of your responsibility to relinquish ownership when done.

Your second example leaks. You can fix it thusly:

self.rootViewController = [[[RootViewController alloc] init] autorelease];

In Mac OS X, whether you use that or your first example is a just a matter of preferred style. With the iPhone, the first example is generally preferred because it doesn't involve adding an object to the autorelease pool. Having said that, since a view controller will probably need to stick around beyond the end of the current event, it makes little difference.

By the way, two answers have mentioned retain counts and they are both correct, but it is better to avoid thinking about retain counts at all and think only in terms of ownership. Retain counts are an implementation detail.

Thank you guys for your answers! I was looking for less "read the memory management guide" type of answers and more of a "dumbed down," "here's the background and difference between these two methods laid out for you" type of answer. I'm familiar with object ownership, retain counts, etc. but I didn't realize WHY using **self.**rootViewController was important, and WHY the second code snippet was leaking... and literally the "behind the scenes" difference between the two. So I came across this post which was I feel was the exact answer I was looking for... (I hope it's accurate!) :) but anyway I'm giving the ole check mark to Philippe because he answered first. I just didn't understand his answer until reading the following post...

http://www.iphonedevsdk.com/forum/iphone-sdk-tutorials/7295-getters-setters-properties-newbie.html

This part was key for me:


So what would happen if you wrote:

self.obj = [[SomeObject alloc] init];

In this case, you're holding onto an object with a retain count of two - the first count comes from the "alloc" and the second one is added by the setter.

To release this variable, you'd have to do something like this:

 [obj release]; self.obj = newValue; 

so that "release" gets called twice on the object. If you omit the extra "release", then when the pointer gets overwritten the object will still be floating around with a retain count of one, and thus doesn't get deallocated. Instant memory leak.


Thanks again!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM