- initWithNibName:bundle: is a message sent to a view (or window) controller in order to create the controller; thus, before the nib is loaded. (Possibly long before. It is not part of the nib loading process.) The view controller that the this method initializes is not created by the nib; when this view controller needs to load its view, it then loads the nib specified in this initializer and uses itself as the "file owner" of the nib.
- initWithCoder: is a message sent to every object in an archive (including a nib) in order to unarchive it; thus, during the nib loading.
- awakeFromNib is a message sent to every object in a nib after all of its outlets are hooked up; thus, after the nib is loaded.
---------------
initWithCoder:
This method is the initializer for all archived objects. As objects stored in nibs are archived objects, this is the initializer used when loading an object from a nib. At the time it's called, the object is being deserialized from the nib, but outlets/actions are not yet hooked up. In the context of UIViewController, it's called when the UIViewController is created from the nib.
initWithNibName:bundle:
This method is the designated initializer for UIViewController classes. It's used whenever you're creating a UIViewController in code. This is in direct contrast to initWithCoder:, which requires the UIViewController to be created from the nib. This method sets up UIViewController to be able to load a nib on demand (namely, when it's view property is accessed).
awakeFromNib
This method is called on every single object that is unarchived from a nib file, after all outlets/actions have been set up. If initWithCoder: is the beginning of the nib unarchiving process, then awakeFromNib is the end. It signals to the object that all objects have been unarchived and that all of its connections are available. In the context of UIViewController, it signals to the view controller that it's now ready to be used.
When creating a UIViewController, you have two options. The first is to create it in code. If you go this way, you use initWithNibName:bundle:. The second is to create one by loading a nib. If you go this way, the nib loading process automatically invokes both initWithCoder: and awakeFromNib.
It's important to note that initWithCoder:/awakeFromN
A common trick to dealing with this sort of situation is to create a "common init" method that's called from both of the initializers:
- (void)commonInit {
_myIvar = @"some value";
_anotherIvar = 3;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self commonInit];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self commonInit];
}
return self;
}
---------------
You only need to send initWithNibName:bundle: when instantiating a view controller, and you only need to override it when implementing custom instantiation behavior in a view-controller subclass. If your subclass only adds a convenience constructor, such as defining myViewController and/or init to send initWithNibName:bundle: with hard-coded values, you don't need to also override initWithNibName:bundle:.
initWithCoder: is a message sent to every object unarchived from an archive in order to unarchive it. The object responds to this message by loading all of its ivar and property values from the archive.
Nibs are archives, so initWithCoder: is sent to every real object in the nib. In such cases, you normally don't need to extract anything from the nib yourself; Apple's implementations handle all of the usual stuff. You only need to extractfrom the archive the custom properties you put into it in the first place (in encodeWithCoder:), so, since you can't encode custom properties in nibs anymore, you don't need to decode them from nibs anymore.
Note that “File's Owner”, “First Responder”, and certain others are fake objects; they do not really exist in the nib. Xcode's nib editor shows these with negative ID numbers.
You don't usually need to override initWithCoder:. The time to do so is when you have an object that needs to have certain properties (e.g., a mutable array or set) filled in from the very beginning, no matter how the object was instantiated (in code or unarchived from a nib).
When you define both initWithCoder: and one or more non-decoding counterparts, there's a high risk of WET[1] code between them. I typically define a commonInit method, put the usual stuff there, and have both init[WithCoder:] methods send [self commonInit] (note: not super!).
awakeFromNib is a message sent to every object in the nib after all of the objects in the nib have been fully loaded, including reconnecting all of their outlet connections in that nib.
There are a few caveats there:
- This doesn't mean that all of your outlets will be filled in. Anything you don't connect in the nib you loaded the object from won't be filled in in that load—those ivars/properties will remain empty. If the object is the File's Owner of another nib, it will need to load that nib in order to complete the outlet connections that are set in that nib. The same goes for loading two or more nibs with the same File's Owner. You cannot create an object in two separate nibs; loading both nibs will produce two of the object. (If it is a singleton, expect a catfight.)
- If you load the object from a nib where a certain outlet is connected, and then load another nib where it is the File's Owner and the same outlet is connected to something else, the new connection will override the first. The same goes for loading two or more nibs with the same File's Owner. In Cocoa Touch, this is harmless, if redundant and possibly confusing (“why is my outlet not hooked up to [object from first nib]?”). In Cocoa, it may causes a leak, since the first object will not necessarily be released. (Cocoa Touch uses KVC to fill in outlet connections; Cocoa does not, but will use accessors if you define them.)
- In Cocoa, awakeFromNib is sent to all of the objects in the nib and the File's Owner. In Cocoa Touch, it is not sent to the File's Owner.
Summary:
- initWithNibName:bundle: is a message sent to a view (or window) controller in order to create the controller; thus, before the nib is loaded. (Possibly long before. It is not part of the nib loading process.)
- initWithCoder: is a message sent to every object in an archive (including a nib) in order to unarchive it; thus, during the nib loading.
- awakeFromNib is a message sent to every object in a nib after all of its outlets are hooked up; thus, after the nib is loaded.
Documentation:
- Archives and Serializations Programming Guide: http://developer.apple.co
m/libra... - Resource Programming Guide: http://developer.apple.co
m/libra... - View Controller Programming Guide for iOS: http://developer.apple.co
m/libra... - NSViewController Class Reference: http://developer.apple.co
m/libra... - UIViewController Class Reference: http://developer.apple.co
m/libra... - NSCoding Protocol Reference: http://developer.apple.co
m/libra... - NSNibAwaking Protocol Reference (Cocoa): http://developer.apple.co
m/libra... - NSObject UIKit Additions Reference (Cocoa Touch):http://developer.apple.co
m/libra...
[1]: https://twitter.com/bored
----------------------------
-init methods need to be called to create an object (and get a pointer to it for the first time), so at the time one object is inited, the others in the NIB may not yet have been created, and you can't access them because you don't have pointers to them. If an object needs to be able to do things with other objects that are in the NIB with it, it can't do that in init. So -awakeFromNib was invented, which gets called after all objects have been created, on each object, so it can do its setup.
initWithCoder: is an initializer for an object that is called when an object needs to be created based on serialized data (created using NSKeyedArchiver or similar classes, usually). NIBs do this for built-in objects (but not, e.g. for "custom view"s, which can be a bit confusing). initWithCoder: takes all its parameters from the NSCoder (usually an NSKeyedUnarchiver) it is given.
Usually, initializers are all funneled through the designated initializer, but you can't do that in the case of initWithCoder, because you don't want subclasses to have to know under which key names the various properties and initializer parameters are saved to disk, which they'd need to pass the parameters to the superclass's initializer. So you just have a parallel hierarchy of initWithCoder: initializers, so subclasses just call initWithCoder: on super.
initWithNibName:owner: is just one initializer. NSWindowController has one, AFAIK. It just happens to load a NIB internally, but it's not really special in anyway.
----------------------------
For example, if you build a custom NSArrayController and want to change its initialization you should overwrite initWithCoder. If you want to customize the initialization of a view you should overwrite awakeFromNib, and so on.
When your program start, the array controllers will not be called with awakeFromNib. And your views will not call initWithCoder.
In Cocoa, the constructor or initialization function for many classes is different. There are more than the ones you mention and part of learning Cocoa is also learning which functions to overwrite if you want a custom initialization.
----------------------------