views:

568

answers:

3

I was looking at some sample code on Jeff LaMarche's excellent blog when I came across the following:

- (void)applicationDidFinishLaunching:(UIApplication*)application
{
    CGRect rect = [[UIScreen mainScreen] bounds];

    window = [[UIWindow alloc] initWithFrame:rect];

    GLViewController *theController = [[GLViewController alloc] init];
    self.controller = theController;
    [theController release];

    // ...
}

In the .h, we see that "window" and "controller" are ivars declared as so:

@interface OpenGLTestAppDelegate : NSObject 
{
    UIWindow            *window;
    GLViewController    *controller;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet GLViewController *controller;
@end

My question is: Why are "window" and "controller" assigned in different ways?

I think I understand why each kind of assignment works (keeping track of retain count), but why are they assigned in different ways? Specifically, why isn't controller assigned in the same way window is with a single line like so without going through the setter:

    controller = [[GLViewController alloc] init];

In general, when would you use the single line method and when would you use the multiple line method?

Thanks.

+1  A: 

The extra code seems to be just because he specifically wants to use the property (setter method). In his implementation (GLView.m), -setController also sets a boolean ivar based on whether the controller responds to (implements) the -setupView: method.

Even so, it would seem that a one-line solution would work just as well:

self.controller = [[[GLViewController alloc] init] autorelease];

The same line as an explicit message send (without dot syntax) works as well:

[self setController:[[[GLViewController alloc] init] autorelease]];

Either approach will leave the new controller with the proper retain count, and still uses the setter property as desired.

(Note: The code in question is linked at the end of this blog post.)


Edit:

Sorry for any confusion. The code has a "GLViewController *controller" ivar and property both in ___PROJECTNAMEASIDENTIFIER___AppDelegate.m and GLView.m, and I was looking at the latter. (In the former, the setter is indeed synthesized, and it will retain the controller. On lines 77-81, you can see the code I mentioned, and he doesn't actually retain the controller — only the AppDelegate retains it.)

In the app delegate code, the synthesized setter will retain the GLViewController, so my one-line replacement advice still stands. One can argue both ways about readability, but for those who understand the retain-release idiom well, I would suggest that the one-line version is much more readable. It communicates the intent succinctly, and even provides an implicit hint that the setter will retain the controller. The extra local variable is really just unnecessary fluff.

Quinn Taylor
Hi Quinn, thanks for answering. Can you point me to the custom setter for the app delegate's "controller" ivar? I see that the view class also has a "controller" ivar and indeed a custom setter, but in the code in my question, "self" refers to the app delegate object, not the view object. Or am I completely confused?
sam
Sorry, my edited answer should help you find what I was talking about. Thanks!
Quinn Taylor
Hi Quinn. For the code in question, wouldn't it be even simpler and clearer to just do: controller = [[GLViewController alloc] init];There is no wondering if the setter is performing any additional steps and no need to fill up the autorelease pool. I understand why all the various options work correctly in terms of retain count, but I don't understand why the original code uses two different methods for assigning ivars one after the other to accomplish the same thing.
sam
+3  A: 

Does he create a custom setter for the controller instance variable?

If so, there may be code which is called when the controller variable is changed through the setter. Merely setting the controller variable with:

controller = [[GLViewController alloc] init];

would not invoke the setter method; however, assigning the newly allocated object to a local variable then setting it with:

self.controller = theController;

would invoke the setter method since it is a shorthand way of writing:

[self setController:theController];

and the extra code in the setter would be executed. This is commonly where you would expect the differentiation between the two methods.

Edit:

Evidently, after taking a look at the code, he doesn't implement a custom setter method, however the method that he has used is still most commonly used when a custom setter method would be implemented.

My guess at the reason behind the extra code would be that he plans to release the variable after allocation, and if assigned to a local variable, he can call the setter method with the local variable and then call release on the local variable afterwards. This would be overall more readable than using

[[self controller] release]

However, it is an odd way to do it, as the synthesized implementation of the setter will retain the instance variable, yet he then releases it once it has been set to the instance variable, and as the release call cancels out the retain call, it would make more sense to set the variable using the one-line method.

Perspx
If you look at the code linked from the blog post, you'll see that he does. Even so, the first and third lines are unnecessary boilerplate. Adding an autorelease makes it much simpler and works exactly the same.
Quinn Taylor
Agreed. I was just explaining the difference between the two implementations presented above. And it's down to taste - some people will prefer to nest the method calls, others prefer to write the extra code to improve readability.
Perspx
Oh man, I must be blind. I don't see the custom setter. I'm referring in my question to the app delegate class, not the view class which also has a "controller" ivar and a custom setter. Can you point me to the custom setter for the app delegate's "controller" ivar? The only methods I see in the app delegate's implementation are applicationDidFinishLaunching and dealloc.
sam
Take a look at my edit.
Perspx
+2  A: 

As Quinn pointed out, the assignment to the controller ivar may be written in one line using autorelease method. The reason to use more verbose version is exactly to avoid autorelease and use manual release instead. This is due to Apple recommendation to minimize the use of autorelease pools on iPhone. So you must store the reference to the newly allocated object in a local variable to release it after a call to setter.

Considering the question when to use direct assignment to an instance variable (as in the case of window ivar) and when to use a setter method (as in the case of controller ivar), it is mostly a question of style, but you better be consistent.

There are two styles of ivar setting:

  1. Always use direct assignment to an ivar. Switch to setter methods only for ivars for which setter must perform some additional work beside assignment.
  2. Always use setter methods for all ivars.

Personally, I think that use of the second style results in more consistent and maintainable code. If some day you realize that your setter must perform more work you should change only the setter, while when using the first style you also should change all occurrences of direct assignment to the setter call.

Just found the good discussion of the issue in another thread: http://stackoverflow.com/questions/549962/instance-variable-method-argument-naming-in-objective-c.

alexstr