Vincent Gable’s Blog

August 15, 2010

My UIViewController Template

Filed under: iPhone,Objective-C,Programming,Sample Code | , , ,
― Vincent Gable on August 15, 2010

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.

Powered by WordPress