In conversations about iPhone development I've noticed that many people, despite being able to get a lot of stuff done in their iPhone projects, feel like the way it all works is magic. I'm going to try to put together a couple of articles about how things work in an attempt to demystify the areas that seem to be the most 'magical'. In this first article I'm discussing application startup. Chaining NIB files will likely be the next article, if you have other ideas I'd be happy to entertain them, email or comments are a fine way to suggest.
I know, getting rid of the magic might be disappointing, but it will make you a better iPhone developer in the end so its worth it :)
To follow along with the discussion, fire up Xcode and create a new view based application called 'Hello'. All the code in this post is pulled from the instantiation of the View-Based Application template.
Like many other C based languages (such as Java, .NET and many others) Objective-C uses the main function as the entry point. Fortunately every template that ships with the iPhone SDK has an implementation of the main function built in. Here is the code for the main method.
As you can see there is not too much here, four lines of code is all it takes to kick off a Cocoa Touch application. Let's take a look at what is happening on each line of code. On the first line the 'top-most' autorelease pool is being created, this is a crucial part of the reference counting memory management scheme for Objective-C objects. I don't have time to cover retain counting in this post but Apple has some decent docs here, and my screencast here has some additional content.
The next line of code invokes the UIApplicationMain function. This function is the source of much of what appears to be magic. The function takes four arguments, the first two are the same arguments that the OS passes into the main function. The next two arguments take a bit more to explain.
The third and fourth arguments are the class names of the principal class, which should be a subclass of UIApplication, and the delegate class, which should implement the UIApplicationDelegate. If you pass in these two class names they will be used to create instances of the two classes, and then the instance of the delegate class will be set as the delegate of the application object.
Since we are passing 'nil' for the final two arguments the function expects the Info.plist to specify a value for the NSMainNibFile key that notes a nib file as the 'main nib file'. This nib file is expected to have the File's Owner's class be UIApplication and have its delegate set to another object in the same nib file. We will look at the nib file in a moment. In the mean time here is the relevant bit out of the Info.plist file.
Back to main, after the UIApplicationMain function returns (which won't be until the application is quitting) the pool is released. The final line where we return the integer we got from UIApplicationMain lets the OS know how the application ended. On the iPhone the return value is largely ignored and UIApplicationMain always returns zero anyway perferring instead to handle error cases via a Unix exit function call. So now you have seen the basic startup code for every iPhone application. Now lets look at in detail at the main nib file and how UIApplicationMain works with it to build the running version of your application.
Main NIB File
Nib files are serialized graphs of objects, the objects that make up your application's user interface. Another way to think about a nib file is like a freeze dried copy of your app's user interface. The objects (like text fields, buttons and labels) that you add to the UI in Interface Builder get written into the nib file. When the nib file is loaded all those objects are reconstituted from the file into real objects.
The objects in the document window are also written to the file, mostly anyway. There are a few exceptions and one that is particularly interesting. The 'File's Owner' object is a proxy to an object that is passed into the nib loading method loadNibNamed:owner: as the second argument. So instead of the File's Owner being in the nib file, a proxy to it is in the nib file and a real object is substituted when the nib file is loaded.
The cool thing about this substitution is that the connections and other configuration we do to the File's Owner is applied to the real object when it is passed in. Let's open up the nib file and take a look, open up the MainWindow.xib. Here is a screenshot of the document window.
Select the File's Owner and bring up the Connections inspector with Command-2. Notice that the inspector (the fig below is a screen shot of it on my box) you can see that the delegate is set. That connection is what sets the delegate of our application object, despite the application object being created in UIApplicationMain. I hope that makes sense because it is so important to understanding many of the other ways that we use nib files, especially with view controllers.
The next question to ask is 'how does Interface Builder know that the File's Owner object has a delegate outlet?' If you keep the File's Owner selected and bring up the Identity inspector (Command-4) you will see something that looks like the next screen shot. So it's not magic, it's not some interesting naming scheme. We tell Interface Builder the class of the File's Owner, when we do that IB looks at the class and gives us access to its outlets and actions so that we can make these connections.
Now that we have looked at the configuration of the File's Owner lets look at the delegate. Select the object called 'Hello App Delegate' (top right hand side in the object view from the screen shot above) and bring up the Identity inspector with Command-4. Notice the class is set to HelloAppDelegate. This object is in the nib file so we never have to create an instance, the one that runs in our application comes from loading this file. See the difference between the File's Owner? We configure both in Interface Builder, but one is created outside the nib file and the other comes from inside the nib file and is reconstituted when the nib file is loaded.
The Application object sends its delegate all sorts of interesting messages (look up UIApplicationDelegate for a full list). In our case though we are interested in the applicationDidFinishLaunching: method since that is the only method implemented for us by the template. Here is the code for the method.
Not much code here, but this is what makes the application 'our' application. The first line puts the content of our view controller into the window, and the second makes it visible and ready to start processing events. Where does this window and view controller come from? Well the nib file of course, who'd want to write all the code to create these objects by hand, and more importantly who'd want to maintain it. Back in Interface Builder, select the 'Hello App Delegate' object and open the connections inspector (Command-2). You should see something that looks like this screen shot.
In the 'Referencing Outlets' you see the incoming connection from the File's Owner. In the top list you see where our view controller and window come from. Move your mouse over the window connection and look in the document window, notice that the 'Window' object is highlighted. That little bit of Core Animation magic is what tells you the objects at both end of the connection. The viewController is connected to the object called 'Hello View Controller'. And these two connections are where the window and view controllers come from in the applicationDidFinishLaunching: method.
Finally, to recap what we have seen so far this diagram shows the basics of what happens during app startup. The main function is invoked by the OS, that fires off the UIApplicationMain function which creates the application object, looks in the Info.plist file for the 'main nib' file and loads that nib file passing the newly created application object in as the File's Owner. The connections made in IB are then applied to the File's Owner and when the application finishes launching the applicationDidFinishLaunching: delegate method is invoked which causes our view controller's view to become visible and ready to take events.
I hope this has helped demystify how iPhone applications start up. If you'd like to grab my copy of the code (only slightly modified from the template) you can grab it here.