Initializers In Objective-C

August 6, 2008

The code for creating new instances of a class generally looks as follows:

1
  SomeClass *ptr = [[SomeClass alloc] init];

In this case, we send a message (alloc) to the recevier (SomeClass) to create a new instance; the object returned from this message, then sends a message (init) to initialize instance variables within the class.

I briefly talked about the importance of the order in which objects are allocated and initialized. You can read more about that here: Memory Management in Objective-C . In this post, I want to introduce how to override the init method of the NSObject class, more specifically, how to work with multiple initializers.

In the init method of the NSObject class, no initialization takes place, it simply returns self .

The basic format of a method to override the initializer looks as follows:

1
2
3
4
5
6
7
8
-(id)init
  {
    if (self = [super init])
    {
      // Initialization code here
    }
    return self;
  }

It’s important to call the super init method first to ensure that instance variables up the inheritance hierarchy are initialized in the proper order (from top to bottom). Also, if your initialize code fails, you need to return nil from the overriden init method. Finally, return a reference to self as shown above.

So let’s assume we have a block of code as follows:

1
2
  SomeClass *ptr = [[SomeClass alloc] init];
  [ptr setStr:@"Testing"];

Here we initialize a new instanc of SomeClass, and follow this with a call to set an instance variable to the specified string (@”Testing). One common means to accomplish this is to create a new initializer in which we pass in the parameter (the string in this case) as part of the original creation of the object. For example:

1
SomeClass *ptr = [[SomeClass alloc] initWithStr: @"Testing"];

We can also take this one step further. Let’s say that we also wanted to initialize an instance variable that was a pointer to a date object (NSDate *). In that case, we might want an additional initilizer that looks as follows:

1
2
SomeClass *ptr = [[SomeClass alloc] initWithStrAndDate:@"Testing"
       date:[NSDate date]];

It quite common for classes to have more than one initializer for creating new objects, allowing variations as shown above. This also implies that the initialization methods need to work in harmony. Designated initializers are the means to achieve this harmonious state.

Designated Initializers
When working with a class that has more than one initialization method (as shown above), it’s important that one of the initializers drives the whole process. Put another way, only one of the initializer methods does the actual work of calling the super class initializer and initializing the instance variables of the object.

This process is actually quite simple. Let’s assume we have a class implementation as follows:

1
2
3
4
5
6
7
8
9
10
@interface SomeClass : NSObject
  {
  	NSString *str;
    NSDate *date;
  } 
 
  // Designated initializer
  -(id)initWithStrAndDate: (NSString *)inStr date:(NSDate *)inDate;
  -(id)initWithStr: (NSString *)inString;
  -(id)init;

The way this should work, is that a call to the initializer init should call initWithStr . A call to the initializer initWithStr should call initWithStrAndDate . Following this process, all the actual initialization work occurs in initWithStrAndDate . This method (the one that does the work) is known as the designated initializer .

A general rule of thumb (although not always the case) is that the designated initializer is the initializer with the most parameters.

So let’s see how this might look within the implementation of a class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@implementation SomeClass
 
  // ==========================
  // = Designated initializer =
  // ==========================
  -(id)initWithStrAndDate: (NSString *)inString date:(NSDate *)inDate
  {
    if (self = [super init])
    {
      [self setStr:inString];
      [self setDate:inDate];
    }
    return self;
  }
 
  -(id)initWithStr: (NSString *)inString
  {
    // Either of these will work
    return [self initWithStrAndDate:inString date:[NSDate date]];
    //  return [self initWithStrAndDate:inString date:nil];
  }
 
  -(id)init
  {
    return [self initWithStr:nil];
  }
 
  ...
  @end

Notice how starting with init , it calls the next initializer in the chain, initWithStr , passing in a default value (in this case, nil). This method then calls the designated initializer, again, passing in a default value, this time for the date. And notice how the designated initializer is the only method that calls super .

Working with multiple initializers is a simple process of ensuring that each initializer calls up through the initialization chain within the class. This ensures that all instance variables are initialized in just one place, and that the super method is called such that all instance variables of classes further up the hierarchy are initialized first (from top down).

Here is a copy of the Initializers Xcode project if would like to try the above example within Xcode.

7 comments

Hi,

very interesting post! And also interesting web site initiative.
One question (I struggled with):

What if my domain is modeled in a way that there is:

- init (no parameters)
- init (a parameter p1)
- init (a parameter p2)

is it p1 to call p2 or viceversa?

Thanks,

by funkyboy on Aug 13, 2008. Reply #

@funkyboy

I’d suggest a different method:

set up a designated initializer

- init (parameter p1, parameter p2)

and have your initialisers call this initialiser with default values for the parameters not passed in.

by stompy on Aug 16, 2008. Reply #

What if you have initializers like:

-(id)init;
-(id)initWithFD:(int)inFD;
-(id)initWithPath( const char * filepath );

where the initWithPath implementation would call open and (if successful) call initWithFD which could mmap the file and (if successful) call init?

by geowar on Apr 1, 2009. Reply #

I am playing around with it, what I ended up doing was:

initWithStr: (NSString*)str;
initWithStr: (NSString*)str andDate: (NSDate*)date

that way when you invoke it it would look like:

initWithStr: @”ABC” andDate: date

by Archimedes Trajano on Jun 22, 2010. Reply #

Can I use something like this?

- (id)init {
if (!(self = [super init])) return nil;
self.isSpecial = NO;
self.index = -1;
self.week = -1;
self.images = [[NSMutableArray alloc] initWithCapacity:25];
self.menu = [[NSMutableArray alloc] initWithCapacity:25];
self.tip = [[NSString alloc] init];
return self;
}

- (id)initWithAttributesIsSpecial:(BOOL)_isSpecial
index:(int)_index
week:(int)_week
images:(NSArray *)_images
menu:(NSArray *)_menu
tip:(NSString *)_tip {
id _day = [[self init] autorelease];
self.isSpecial = _isSpecial;
self.index = _index;
self.week = _week;
[_day addImages:_images];
[_day addRecipes:_menu];
self.tip = _tip;
r

by Bruno on Mar 28, 2011. Reply #

should it not be

if (self== [super init]){

}

by Jai Prakash on Jul 19, 2011. Reply #

The syntax can be confusing. It is essentially calling [super init] and assigning to self. The if statement checks to see if the result is nil (that is, self is nil).

Here is an equivalent that may make more sense:

self = [super init];
if(self) 
{
  ...
}

by John Muchow on Jul 19, 2011. Reply #

Leave a Comment