Objective-C: Initializers

June 30, 2008

Note: What follows is another post in an on-going series for developers who are interested in learning to write applications for the iPhone. The entire series can be found here: iPhone Developer .

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

  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:

-(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:

  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:

  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:

  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:

  @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:

  @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.

4 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 Jun 30, 2008 at 7:20 am. #

I’m not sure what you mean. Do you mean three initializer methods, one that takes no parameter, one that takes one data type (say NSSinteger) and another that takes a different data type, say NSDate?

In that case, you could still use the code above, passing in nil for any parameters that you don’t have. For instance, if you want to initialize a date but not a string, pass in nil for the string. And vice-versa, if you want to initialize a string but not the date, pass in nil for the date.

Hope that helps.

by john on Jun 30, 2008 at 4:58 pm. #

@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 15, 2008 at 2:42 am. #

Comments are closed on this post. You can comment on this same post which has been moved to the iPhone Developer Tips blog: http://iphonedevelopertips.com/objective-c/initializers.html

by john on Aug 16, 2008 at 7:35 pm. #