Cocoa Tutorial: awakeFromNib vs applicationDidFinishLaunching
When developing an application in Objective-C and using Cocoa, there is a lot of “magic” that happens in the background. As we get more comfortable with the language and the APIs, we begin to discover the source of that magic and understand not only WHY it works but HOW it works.
One of those areas is the initialization and callbacks from the nib files to my code. Normally, when I want a controller to do something after the NIB/XIB has loaded, I add the method -(void)awakeFromNib and know that I will receive a call when all of the connections into the NIB/XIB are complete. But on what object does this get called and how?
In another case, I can be notified when the application has finished launching. This method, -(void)applicationDidFinishLaunching:(NSNotification*)aNotification gets called after the MainMenu.[nib|xib] has finished loading. How does this method get called and on who does it get called?
And the third question. If I am using both of these spells above, which gets called first and why?
awakeFromNib is called on every object that is initialized or referenced from within a nib file. For example, take a look at the following nib file:
In this nib file, I have references to two pieces of custom code: The Window Controller and the Window itself. So which of these two gets the awakeFromNib call? Both do.
Specifically, when a nib file has completed initializing all of the objects, it then loops back through every object referenced in the nib and if the object responds to awakeFromNib, it calls awakeFromNib on the object. Therefore, you can have logic in any or all of your objects in the awakeFromNib call.
In the nib above, which object would receive a applicationDidFinishLaunching: call? None of them. The nib in that example is not set up to be the primary nib for an application. When a nib is designed to be the primary or initial nib for an application it will have a reference to NSApplication in it.
In this example you can see the reference to the NSApplication in the nib. This nib is designed to be the initial nib loaded by the application. This can be confirmed by looking at the File’s Owner class type and see that it points to NSApplication rather than an NSObject or some custom controller class (Image 3). So who gets the applicationDidFinishLaunching: call? While you cannot see it in the image above, the object ‘AppDelegate’ is referenced as the delegate for the NSApplication.
Therefore, when the application has indeed finished launching, then this method on the AppDelegate and only the AppDelegate will be called. Only one object gets this method per application launch.
In the second example above, it is possible to have an awakeFromNib method and an applicationDidFinishLaunching: method in the AppDelegate. Which would be called first and why?
The answer is that the awakeFromNib will be called first. When the nib file has been initialized, each object referenced in the nib will be looped through and they will all receive an awakeFromNib call if they respond to the message. After all of that is done then the Application’s delegate will receive the applicationDidFinishLaunching: call. This is the notice that everything is loaded and that the application is ready to start receiving user input.
If your code is localized to only the object you are working with then putting the code in the awakeFromNib is the perfect place for it. However, if your code is going to be manipulating more than one object in the nib then it is probably safer to put it in the applicationDidFinishLaunching: instead. This will insure that everything is loaded, referenced and ready to go.
Who is calling those methods? Is it the NSApplication instance or some other part of the runtime?
NSApplication calls the applicationDidFinishLaunching.
As for the awakeFromNib it can vary greatly but on startup it is generally the NSApplication also. Later in the applications life it is usually you :)
Also worth noting is that during the -awakeFromNib round your application’s icon will continue to bounce in the Dock. By the time the -applicationDidFinishLaunching: notification is sent, the bouncing will have ended.
You can use this knowledge to influence the “perceived” launch time of your application, by segregating heavy initialization code into the latter method where it makes sense, reducing the number of launch-time Dock bounces.
By the way, Marcus and Matt, the blog is a great read. Keep the technical stuff coming!
“When a nib is designed to be the primary or initial nib for an application it will have a reference to NSApplication in it.
In this example you can see the reference to the NSApplication in the nib.”
I am probably being a bit thick (fairly new to Mac development), but where should I see this reference to NSApplication? I thought you meant the icon labelled Application, but that’s in both examples. Do you mean the MainMenu instead? Sorry if I’m asking a dumb question here.
Also important: awakeFromNib can be called multiple times on the controller if you use the same controller for several nibs – say, you’re using the app delegate as the owner of both the app’s About Box and preferences dialog. So you’ll need an extra guard test if you use awakeFromNib for anything but initializing the nib objects,
Second important call in the app delegate is applicationWillFinishLaunching, which will be called earlier; in fact, while the icon is still bouncing in the Dock (applicationDidFinishLaunching is called when the icon stops bouncing). So this is a place to early-initialize application stuff.
If your app is opened by double-clicking on a document icon, application:openFile: will be called on the app delegate _before_ applicationWillFinishLaunching is called. So if you implement that method too, you should do something like
[self performSelector:… withObject:filename afterDelay:0.0] to act on the file after the run loop gets going.
Finally, the main run loop is started after applicationWillFinishLaunching returns.
I’m confused about the statement ‘When a nib is designed to be the primary or initial nib for an application it will have a reference to NSApplication in it’. All nibs have a reference to NSApplication. It ‘s the one called ‘Application’.
Granted, I think that icon is new in IB3.0, so possibly you mean that the File’s owner is set to NSApplication, but then I don’t see how ‘In this [second] example you can see the reference to the NSApplication in the nib’
Could you explain a bit more?
Thank you everyone for the great comments. In reference to Cromagnon’s question. I have added Image 3 to show how you can see the difference. Specifically, if you look at the class type of the File Owner, you will see that it is an NSApplication rather than another object type. Also, applications usually only have one Main Menu reference and that will also be in the primary nib.
Thank you for the tip on application load. I never put those two together before but now that you have for me it makes perfect sense.
Yes I could have written that part better. Hopefully with the addition of the new image it will be easier to understand.
A couple clarifications…
-applicationDidFinishLaunching: will be sent to the application delegate as described above. However, it is a notification as well. NSApplication sends the notification NSApplicationDidFinishLaunchingNotification. The application’s delegate is automatically registered for this notification if it implements -applicationDidFinishLaunching:. But any object can register for that notification.
In general, most delegate methods in Cocoa that have Will or Did in their names are convenience covers for notifications. (The definitive way to tell is in the delegate method takes a single (NSNotification*) parameter.) For all these methods, there’s a corresponding string constant for the notification name and any object can register to receive those notifications with NSNotificationCenter. Multiple objects can receive any notification.
Of course, to usefully register an object to receive the NSApplicationDidFinishLaunchingNotification, you must do it early enough to know that that notification has not already been sent…
Someone asked who sends these messages.
-awakeFromNib is sent by the nib loading machinery (which is implemented as a category addition to NSBundle in AppKit. It’s declared in AppKit/NSNibLoading.h) -[NSBundle loadNibFile:externalNameTable:withZone:], specifically, will load the nib, get it all set up and connected, and invoke -awakeFromNib on all relevant objects.
-applicationDidFinishLaunching: is invoked indirectly by NSNotificationCenter. NSApplication posts the notification.
[…] is more detailed explanation at Cocoa Is My Girlfriend. Try reading it at least once. It is really well […]
So say LoginViewController has an -(FBSession *) didLogout:; method (madeup method name as I forget the exact name)… what is the proper way to implement RootViewController catching these notifications? I want certain functionality to not be available if FBSession’s session (for facebook) is logged out. I actually want that to be app wide, but using this as an example.
If the notifications are true NSNotifications then any controller can request to be notified of them using the NSNotificationCenter -addObserver… methods. You just need to figure out the names of these methods and you are good to go.