I haven’t formalized this as a proper Xcode template, but this is what’s in my PrototypeViewController.m
file that I copy/paste over the UIViewController
subclasses Xcode makes.
@interface MyViewController () @end @implementation MyViewController - (void) releaseViewObjects; { if([[self superclass] instancesRespondToSelector:@selector(releaseViewObjects)]) [(id)super releaseViewObjects]; } - (void)viewDidUnload; { [super viewDidUnload]; [self releaseViewObjects]; } - (void)dealloc; { [self releaseViewObjects]; [super dealloc]; } @end
Here are the reasons why, in no particular order:
Commented-Out Code is Evil
Littering source code with “comments” full of crufty, obsolete, or unimplemented code is not a good thing. Xcode’s default template is full of commented-out code. If you’re totally new to the platform, starting from the templates aren’t a bad way to learn. But in my experiance, they do harm to a production code-base, by injecting hundreds of lines of commented-out code into a project.
Share code between viewDidUnload
and dealloc
With releaseViewObjects
In my world, releaseViewObjects
is solely responsible for cleaning up every IBOutlet
, and any objects created in viewDidLoad
.
There are technical reasons why this is a little scary. Calling a method in dealloc
is potentially risky, because the object may be in an invalid half-torn-down state, and because Dark Runtime Magicks could be afoot (see Using Accessors in Init and Dealloc.)
But in my experience, such bugs, although as scary as they sound, are rare corner-cases and still quite fixable. But every UIViewController
needs to clean up after itself, so simplifying the universally common case is a net win.
The if([[self superclass] instancesRespondToSelector:@selector(releaseViewObjects)])
test wouldn’t be necessary if I added another class between UIViewController
and my real code, so that I was sure my class’ super
implemented releaseViewObjects
. But adding a subclass just to implement one empty method, to avoid a two-line test, isn’t worth it.
The (id)super
cast is intentional, to prevent compiler warnings.
I have to use the more complex [[self superclass] instancesRespondToSelector:
test, because -[super respondsToSelector:]
doesn’t work.
I Won’t Really Get To didReceiveMemoryWarning
I’m not proud to admit this, but it’s true. We’ve all been told that a good iPhone program must release resources when it gets a memory warning, or else it will be killed. But in practice, there have always been better places to spend my time (or at least it sure feels that way!) Spending a few hours in Instruments to fix leaks prevents memory warnings in the first place, and that’s a bigger win.
Besides, 80% of what didReceiveMemoryWarning
would do is handled in releaseViewObjects
, which is automatically called by the default implementation.
So I break with Xcode and leave didReceiveMemoryWarning
out of my template, because the default class won’t use it.
What About init
?
I don’t have a default init(With…)
method. I try to use autorelease
-ed objects everywhere I can, so I’m more comfortable implementing +[MyViewController viewControllerForFoo:]
.
But I don’t have a default constructor of any kind, because a constructor should take every value it needs, and I don’t know what these values are until I’ve written a bit more of the class. It’s a chicken and egg problem.
Once I’ve written out a bit more of the class, I’ll usually build something that looks like:
+ (RouteMapViewController*) routeMapViewControllerWithWaypoints:(NSArray*)waypoints mapRegion:(MKCoordinateRegion)region; { RouteMapViewController *vc = [[[self class] new] autorelease]; vc.title = NSLocalizedString(@"The Path",@""); vc.hidesBottomBarWhenPushed = YES; vc.waypoints = waypoints; vc.mapRegion = region; return vc; }
For what it’s worth I use this pattern to implement a 0-argument -init
.
Empty Class Extension
Class extensions are the best way to have “private” things in Objective-C. They let the compiler catch objects using another object’s private methods. They let a class have publicly readonly
, but internally readwrite
, properties.
Bottom line: every nontrivial object I’ve written uses them, so they’re in my template.
Nothing Else (For Now)
My template is smaller than Xcode’s. That is by design. Outside of esoteric contests, having less code to maintain is a good thing. So I prefer a template that tries very hard to avoid adding code I don’t need.
Do you disagree with any of my choices? Please leave a comment explaining why.
C Comment Trivia
― Vincent Gable on December 8, 2008
//I'll bet \
you \
didn't \
know \
you \
could \
comment \
like \
this \
in \
C/C++/Objective-C
Well, you can. Finding out was a lot of fun, believe me.
At some point, I’m not sure when, I unknowingly put a ‘\’ at the end of a block of
//
comments. That commented out the method-call the block was referring to. As you might guess from the fact that it had a big comment talking about why it was there, this was an important method call. I spent more time then I want to admit debugging and staring at my code trying to see the problem. Finally I noticed “hey, the color of that code is like a comment, even though it’s not in a comment”.