Reputation: 6940
I made a test app to understand how exactly init methods work. In my simple UIViewController
I call the following:
- (id)init {
self = [super init];
self.propertyArray = [NSArray new];
NSLog(@"init called");
return self;
}
The above does not print any values in NSLog. However, when I write :
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
NSLog(@"init called");
self.propertyArray = [NSArray new];
return self;
}
It does print "init called" in console. So my question is: why is the init method called and the other is not? Which one do I have to use, when i want to do my stuff before the view loads (and any other methods called)?
Any explanation will be appreciated, thanks.
Upvotes: 1
Views: 94
Reputation: 2006
To begin with, you mention ViewController in your question. A UIViewController's designated initializer is initWithNibName:bundle:
You would never want to override just init on a UIViewController.
There is a lifecycle for each object:
When initializing in code, you have the designated initializer. Which you can find in the documentation for that class. For NSObject derived classes this would be init:
- (id)init
{
self = [super init];
if (self) {
// perform initialization code here
}
return self;
}
All objects that are deserialized using NSKeyUnrchiving, which is what happens in the case of Storyboard's or NIBs(XIBs), get decoded. This process uses the initWithCoder initializer and happens during the unarchiving process:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// perform initialization code here
}
return self;
}
It is common, because of this lifecycle, to create a shared initializer that gets called from each initializer:
- (void)sharedInit
{
// do init stuff here
}
- (id)init
{
self = [super init];
if (self) {
[self sharedInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self sharedInit];
}
return self;
}
To take it one step further. In the case of Storyboard's and XIBs, if you want to perform initialization or updates AFTER the unarchiving is completed and to guarantee all your outlets and actions are connected, you would use awakeFromNib:
- (void)awakeFromNib
{
// do init or other stuff to be done after class is loaded from Interface Builder
}
Upvotes: 2
Reputation: 100602
The different init
s are different constructors. As in any other language, an instance is instantiated by the most appropriate constructor. That's initWithCoder:
when restoring from an archive.
As a style point, note that use of self.propertyArray
in a constructor is considered bad form. Consider what would happen if a subclass overrode setPropertyArray:
. You'd be making a method call to an incompletely instantiated object. Instead you should access the instance variable directly, and perform the idiomatic if(self)
check to ensure it is safe to do so.
Upvotes: 1
Reputation: 25459
When you load view controller from nib file (and storyboard) it uses initWithCoder:
so in your example this is why it call this method.
If you create your view controller programatically this method won't work and you should override initWithFrame:
initialiser instead and also you should create view controller by calling
[[UIViewController alloc] initWithFrame:...];
Upvotes: 1
Reputation: 726479
When a class is instantiated in your code, you pick which initializer to call, depending on your needs. When a class is instantiated through framework code, you need to consult the documentation to find out what initializer would be called.
The reason that you see the behavior that you describe is that your view controller is in a storyboard. According to Cocoa documentation, when a view controller is instantiated through a storyboard, its initWithCoder:
initializer is called. In general, this call is performed when an object gets deserialized.
Note that it is common to check the result of self = [super initWithCoder:aDecoder];
assignment, and skip further initialization when self
is set to nil
.
Upvotes: 1