views:

310

answers:

5

Hello,

I have always wondered why all apple code samples use code like this:

UINavigationController *aNavigationController = [[UINavigationController alloc]
          initWithRootViewController:rootViewController];

self.navigationController = aNavigationController;

[self.view addSubview:[navigationController view]];

[aNavigationController release];

They always create a local variable and assign it to the ivar why don't they simply do this:

self.navigationController = [[UINavigationController alloc]
          initWithRootViewController:rootViewController];;

[self.view addSubview:[navigationController view]];

[navigationController release];

Is there any other reason, besides it is easier to understand?. Is this a best practice?.

-Oscar

+1  A: 

The differences in the first lines is that Apple's version separates object creation and assignment to an ivar whereas yours puts the the two together. Conceptually, Apple's version is slightly easier to understand. It is not a best practice to my knowledge.

Giao
+1  A: 

Both versions miss a check for a nil value:

(Assuming self.navigationController is a property that retains it's value)

self.navigationController = [[UINavigationController alloc]
    initWithRootViewController:rootViewController];
if (self.navigationController != nil) {
    [self.view addSubview: navigationController.view;
    [self.navigationController release];
}

You could argue this is style, but in my opinion it leads to less buggy code.

St3fan
+1  A: 

It may be the same thing as the top example, but there's a chance it won't be.

Remember that

self.navigationController = aNavigationController;

is the same as

[self setNavigationController:aNavigationController];

and you don't know what's going on inside that setNavigationController method. It could be initializing a different object and setting that as the iVar, which you then release, causing a crash.

kubi
@OscarMK, The second code bit is fine for retain based properties but isn't suitable for copy based properties like kubi mentions.
Deepak
I know they are the same, but if you are using synthesize and not overriding setNavigationController isn't it guaranteed to assign it the value you are pasing and send it the retain message? (assumming you declared your property with retain). Additionally both methods use the same self.navigationController so I am not sure what does this have to do with the question?
OscarMk
I can't think of any way it would be cause issues if the property is `retain`, but if it's `copy` or `assign` it'll definitely cause issues. I wouldn't write it like this, my guess is that it's going to come back and bite you someday, and there's no reason not to do it the first way you listed.
kubi
A: 

Since it's clear that the code is using a UINavigationController instance variable. Then there wouldn't be no reason to do:

self.navigationController = aNavigationController

If you're not retaining it.

But, if you do it like this:

 self.navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];

Afterwards, if you release it like this:

[navigationController release];

It will appear to be that we are releasing the instance variable that's supposed to be retained for the lifetime of the current class that initialize the navigation controller. So, it's prone to mistake, that will make beginners think that it should only be released in the dealloc method.

Both approach will have retain count of 0 in the end. If at the dealloc implementation:

[navigationController release]; // 1 for the ivar
[super dealloc]; // 0 for the retained subviews
Jesse Armand
The retain count would be 3 for both assignments(alloc, retain and addsubview), also because the retain count 3, when you call release it would still have 2, so even if this is removed from view it would still have 1, which then you release at dealloc.
OscarMk
Rob's answer makes it clear. But, Apple's sample code is more understandable compared to your code, for those who don't pay attention to the retain count. For beginners it's counter-intuitive, if navigationController is released twice (there and in the dealloc).They all have the same results. The retain count will be balanced. But, the sample code makes it more clear that you're doing an alloc/init and release on the same variable. Then, the instance variable memory management is handled in other sections of the code.
Jesse Armand
+1  A: 

Your replacement code is incorrect, and thus illustrates the problem that Apple is trying to prevent. Here is your code:

self.navigationController = [[UINavigationController alloc]
      initWithRootViewController:rootViewController];

[self.view addSubview:[navigationController view]];

[navigationController release];

You left out the "self." in your references. Perhaps you meant to access the ivar directly, but in that case you have created very confusing code by mixing accessors and direct ivar access (and violated the cardinal rule by using direct ivar access outside an accessor). If not, then you meant to write this:

self.navigationController = [[UINavigationController alloc]
      initWithRootViewController:rootViewController];

[self.view addSubview:[self.navigationController view]];

[self.navigationController release];

That last line is very wrong. Never send -release to the result of a method call. So no, the way you're doing it isn't correct.

That said, Apple and I disagree on how to do this. Here's how I do it:

self.navigationController = [[[UINavigationController alloc]
      initWithRootViewController:rootViewController] autorelease;

[self.view addSubview:[self.navigationController view]];

I like -autorelease because I find it prevents errors. The further apart the alloc and release get, the more likely a developer will inject a memory leak (by adding a "return" for instance). autorelease avoids this by keeping the retain and release together, making the intent to use this as a temporary variable more clear, and generally makes code review much easier.

Apple tends to disagree with me in their example code because they're emphasizing performance by using release versus autorelease. I find this to be the wrong optimization, since this object won't be deallocated during this run loop in either case (so no memory is saved), and I believe the very small performance penalty of autorelease is more than made up for by the reduction in memory leaks due to incorrect use of release.

The autorelease vs. release debate is filled with shades of gray (I certainly use release directly in loops), and different developers have different approaches, but your replacement code isn't the right way to do it in either case.

Rob Napier
Thank you great answer. The only thing that is not clear to me is why should I never send a release to the result of the accessor call?
OscarMk
You should never release the result of a method call. An accessor is just a specific instance of a method call. You should only release things that you retained. You're assuming, for instance, that the object you get back from -foo is the same object you passed to setFoo:. That may not be true (caching or copying may change this) You should not rely on internal implementation details of the accessor; you may change them later. Just follow the three magic words, and memory management won't be a problem. http://robnapier.net/blog/three-magic-words-6
Rob Napier