Advertisements

Extension 13: Complex Initializers


The subject of initializer methods deserves a bit of special attention. Most importantly, in addition to the default initializer method that is inherited from NSObject, most classes also have other initializers that take certain arguments. All initializers, however, follow the same pattern. Let’s look at an example.

Convenience Initializers

Additional initializers are often known as convenience initializers because you can initialize the object and set some or all of its instance variables (and perhaps other values) in one method call. To begin, let’s take a look at some of NSString‘s initializers. These include initWithString:, which takes an NSString as an argument and creates a new string from that, initWithFormat:, which takes a formatted string and a corresponding number of arguments, and plugs in the proper values for each format specifier, and returns the completed string, and initWithContentsOfFile:encoding:error:, which creates a string out of any text file. The latter example is a perfect example of the convenience nature of these methods—doing the same with C library functions would be a lot more difficult. Note also that NSString has class methods that do the same thing (except that according to the memory management rules, the values are autoreleased).

If we were to create a Rectangle class, you might have an initializer like this:

-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super init]))
        return nil;
    self.width = w;
    self.height = h;
    return self;
}

Not too much that’s new there. After the initialization, we assign proper values to our instance variables and return. Let’s add another initializer:

-(id)initWithWidth:(float)w andArea:(float)a {
    if(!(self = [super init]))
        return nil;
    self.width = w;
	self.height = (a / w);
	return self;
}

Not much that’s new here. We’ve simply created another convenience initializer.

The Designated Initializer

The interesting bit comes when we decide to subclass our Rectangle class. Suppose we create a ColoredRectangle, or (and this is perhaps a bad example) ColoredCube. We would be adding new instance variables—at least a color ivar, and maybe a further depth measurement. Normally, the subclass would have to override all of those methods, and if the superclass added another initializer, that would mean that all subclasses would have to override that as well.

Fortunately, Cocoa has a solution—the designated initializer. Typically the one with the most arguments, all the parallel initializers call that one, and the subclasses all use the designated initializers in their calls to super. Choose an initializer to be the designated initializer, and follow through with it:

// Rectangle's initializers
-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super init]))
        return nil;
    self.width = w;
    self.height = h;
    return self;
}

-(id)initWithWidth:(float)w andArea:(float)a {
    if(!(self = [self initWithWidth:w andHeight:(a / w)]))
        return nil;
	return self;
}

// ColoredCube's initializers
-(id)initWithWidth:(float)w andHeight:(float)h {
    if(!(self = [super initWithWidth:w andHeight:h]))
        return nil;
    self.depth = 1.0;    // Default value
    self.color = [UIColor redColor];    // Ditto
    return self;
}

-(id)initWithWidth:(float)w height:(float)h depth:(float)d andColor:(UIColor *)c {
    if(!(self = [super initWithWidth:w andHeight:h]))
        return nil;
    self.depth = d;
    self.color = c;
    return self;
}

Note here that we only have to override the default initializer in the subclass, because all the other inherited initializers will then call on that overridden initializer. We can also add more initializers as necessary.

By creating a default initializer (note that there is not specific syntax to denote the designated initializer—it’s up to the programmer to determine which one is the designated and stick with it), it makes subclassing a lot easier.

Advertisements
Leave a comment

7 Comments

  1. Craig March

     /  July 16, 2011

    I’m still learning iOS, however, shouldn’t ColouredCube’s initWithWidth:height:depth:andColor: be calling it’s own designated initialiser and not that of it’s superclass?

    E.g. self initWithWidth:andHeight: and not super initWithWidth:andHeight:

    Reply
    • Generally, custom initializers call the super version to do the “low-level” setup that the class doesn’t care about, then adds custom setup code after. If ColouredCube calls its own initializer, you would also end up with an infinite loop of the method calling the method calling the method calling the method calling itself forever…or at least until the power eventually goes out.

      Reply
      • Craig March

         /  July 19, 2011

        Thanks for the reply.

        I can see how it might come down to how you have designed your classes, although I can’t picture how an infinite loop would occur.

        Just for completeness I found the following in the iOS Dev Library under Allocating and Initializing Objects – Constraints and Conventions:

        “In brief, if you are implementing a new designated initializer, it must invoke the superclass’s designated initializer. If you are implementing any other initializer, it should invoke its own class’s designated initializer, or another of its own initializers that ultimately invokes the designated initializer.”

        http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocAllocInit.html

        Reply
        • In general, yes. The infinite loop would occur if initWithWidth:andHeight: called itself, which is what I understood your initial response to be. But in fact, initWithWidth:height:depth:andColor: is a case where the rules can be bent a little. If you notice in initWithWidth:andHeight:, we are assigning dummy values to the variables, including making a method call to UIColor. That’s a small overhead, but an overhead nonetheless. There’s no issue, in this case, to directly set the new values in, without wasting some clock cycles.

          Reply
          • Craig is absolutely correct here for anyone else learning about initializers. The designated initializer is typically the one with the most arguments, is the only initializer that calls super init, and is called by other convenience initializers with any default values passed in. Convenience initializers should generally not set any properties/state themselves, but in some cases may contain logic that prepares arguments to be passed into the designated initializer.

  2. Craig March

     /  July 20, 2011

    I see. Thanks.

    Reply
  1. Learn Objective-C in 24 Days « Programming for iOS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Advertisements
  • Welcome

    My goal is to make CupsOfCocoa into a beautiful source for beginners to the iPhone platform to get started. Subscribe below for more, and stay tuned!

  • Contact Me

    If you need to contact me for any reason, feel free to send me an email.
  • The Giving Spirit

    If you've found this site helpful, would you consider donating a little sum? Any amount is appreciated...Thanks so much!

  • Roadmap

  • Enter your email address to follow this blog and receive notifications of new posts by email.

    Join 222 other followers

  • Back to the Past

    April 2011
    S M T W T F S
    « Mar   May »
     12
    3456789
    10111213141516
    17181920212223
    24252627282930
  • Time Machine

  • You count!

    • 621,662 views
  • Worldwide Stats

    free counters
  • Advertisements
%d bloggers like this: