The Leg Bone’s connected to the…???


No matter how many times I tell my four year old back, up she continues to play kiss the door knob. No matter how many times I warn my 9 year old to thumb-tack her book-bag to her spine in the morning, she continues to show up at school sans knapsack. I somehow believe my kids enjoy hard times, appreciate the torture of incinerating your appendages on the hot oven door, and outright live for the moment somebody will come crashing through the doorway embedding the door handle in a young forehead. Then I realize there are certain things that they won’t include in the text books… certain things they won’t teach in class or during an online tutorial. These are the things you have to find out by smashing your nose, breaking your arm, and making an absolute fool of yourself. It’s the first step in agile development is it not? It’s the red before the green-refactor, isn’t it? It is amazing to watch the test driven antics in somebody less than half your age.

Before I allow George Killian to seep deep enough into my blood stream to rip the original intention out of my cerebrum let me explain what I just learned. Apple’s Interface Builder != Springframework. Sure it tastes like apple pie but there are no apples in it. Here’s the deal. You would think (or you would if you wore an argyle sweater like me) that wiring a UIImageView to your controller would be sufficient enough to make that same UIImageView available when you loaded the controller from a nib file in the main bundle wouldn’t you? [Yes you would because you’re me in this hodge-podge example… just play along! I don’t care if you never heard of a nib file. Nod your head and say uh-huh…] Calling initFromNib on your controller will read in the nib file and eventually perform the internal connections/plumbing but your next statement will be in for a surprise if it attempts to inspect or dig values out of the controller that are connected to other things with Interface Builder’s connect the dots magic. Here’s what I mean. Create an arbitrary UIController named LegBone and give it a HipBone IBOutlet property. (Create a HipBone class in your project.) Create a nib file, open it in Interface Builder and set it’s FilesOwner to LegBone. Drag and drop an object from the library into the nib file’s main window and then connect the legbone to the hip bone. In your app delegate, instantiate the LegBone using the nib file and declare a local variable of type hip-bone while setting it equal to the property from LegBone on the following line. Run all of this in the debugger and watch as XCode reports 0x0 (or null, or is it nil? What’s the doggone difference and can we settle on a universal nomenclature for a value that doesn’t exist across all programming languages???!!!) when you step to and hover over the LegBone.hipbone property assignment. It should look roughly like this (not compiled or cross checked due to laziness):

LegBone.mm

@interface LegBone : UIViewController
   IBOutlet *HipBone hipbone;

@property (nonatomic, retain) IBOutlet *HipBone hipbone;
@end

MyAppDelegate.mm

@implementation MyAppDelegate

- (void)applicationDidFinishLaunching:(UIApplication *)application {
	LegBone *myLeg  = [[LegBone alloc] 
								 initWithNibName:@"LegBone" bundle:[NSBundle mainBundle]];
        HipBone *hip = [myLeg.hipBone];
        NSLog(@"My hip is %@", hip);
}
@end

Of course after catching the problem the obvious thing to do here is to instantiate the hip in your delegate and set it on the legbone since Cocoa is too lazy/slow/unwilling to do it for you. That brings me to my next surprise. I caught this immediately afterwards but it could have easily been one of those 2 day investigations that check everything but the obvious. Setting the hip bone programmatically without clearing the connect-the-dots magic in InYourFace Buildher will only be undone by the runtime when it finally gets around to connecting your dots during runtime. In other words, say you have this:

@implementation MyAppDelegate

- (void)applicationDidFinishLaunching:(UIApplication *)application {
	LegBone *myLeg  = [[LegBone alloc] 
								 initWithNibName:@"LegBone" bundle:[NSBundle mainBundle]];
        HipBone *hip = [[HipBone alloc] init];
        myLeg.hipBone = hip;
        NSLog(@"My hip is %@", hip);
}
@end

And pretend you forgot to remove the HipBone object in Innerplace Biller. You’ll fix the nil pointer in the app delegate but then inside LegBone you will be talking to a different hip. For a beginner like myself, trial and error will likely never show the core problem. You have to know what to look for. You have to know that wiring is performed at runtime and possibly at a later stage or in an entirely different method. It’s like Dr. House says, “When something doesn’t make sense, one of your assumptions is wrong.” In this case you would assume that initializing a controller from a pre-wired nib will instantiate all of your nib file’s internal objects and prime all of the setters. You would assume that these things happen before control returns to the next statement as it would if you were loading a Spring beand factory and asking for a bean. That’s all for now. I’m going night-night.

One thought on “The Leg Bone’s connected to the…???

  1. Yeah, instantiating an object via the NIB file and treating like a java bean does not work that well. I use the NIB to instantiate my entire application context and use the “awakeFromNib” callback that is on NSObject. Once that method is invoked all of the properties on that object are valid.

    It has to become a very event driven process. That includes views that are instantiated on view controllers. Do not do anything with the views until the viewDidLoad method is invoked.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s