Multiple initializers

Create a new file: a subclass of BNRAppliance named BNROwnedAppliance. In BNROwnedAppliance.h, add a mutable set of owner names and three methods.

#​i​m​p​o​r​t​ ​"​B​N​R​A​p​p​l​i​a​n​c​e​.​h​"​

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​O​w​n​e​d​A​p​p​l​i​a​n​c​e​ ​:​ ​B​N​R​A​p​p​l​i​a​n​c​e​ ​
@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​N​S​S​e​t​ ​*​o​w​n​e​r​N​a​m​e​s​;​
-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​P​r​o​d​u​c​t​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​p​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​r​s​t​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​;​
-​ ​(​v​o​i​d​)​a​d​d​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​;​
-​ ​(​v​o​i​d​)​r​e​m​o​v​e​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​;​

@​e​n​d​

Notice that one of the methods that you have declared is an initializer that takes two arguments.

Implement the methods in BNROwnedAppliance.m:

#​i​m​p​o​r​t​ ​"​B​N​R​O​w​n​e​d​A​p​p​l​i​a​n​c​e​.​h​"​

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​O​w​n​e​d​A​p​p​l​i​a​n​c​e​ ​(​)​
{​
 ​ ​ ​ ​N​S​M​u​t​a​b​l​e​S​e​t​ ​*​_​o​w​n​e​r​N​a​m​e​s​;​
}​
@​e​n​d​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​B​N​R​O​w​n​e​d​A​p​p​l​i​a​n​c​e​

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​P​r​o​d​u​c​t​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​p​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​r​s​t​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​
{​
 ​ ​ ​ ​/​/​ ​C​a​l​l​ ​t​h​e​ ​s​u​p​e​r​c​l​a​s​s​'​s​ ​i​n​i​t​i​a​l​i​z​e​r​
 ​ ​ ​ ​i​f​ ​(​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​P​r​o​d​u​c​t​N​a​m​e​:​p​n​]​)​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​ ​s​e​t​ ​t​o​ ​h​o​l​d​ ​o​w​n​e​r​s​ ​n​a​m​e​s​
 ​ ​ ​ ​ ​ ​ ​ ​_​o​w​n​e​r​N​a​m​e​s​ ​=​ ​[​[​N​S​M​u​t​a​b​l​e​S​e​t​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

 ​ ​ ​ ​ ​ ​ ​ ​/​/​ ​I​s​ ​t​h​e​ ​f​i​r​s​t​ ​o​w​n​e​r​ ​n​a​m​e​ ​n​o​n​-​n​i​l​?​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​n​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​_​o​w​n​e​r​N​a​m​e​s​ ​a​d​d​O​b​j​e​c​t​:​n​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
 ​ ​ ​ ​/​/​ ​R​e​t​u​r​n​ ​a​ ​p​o​i​n​t​e​r​ ​t​o​ ​t​h​e​ ​n​e​w​ ​o​b​j​e​c​t​
 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

-​ ​(​v​o​i​d​)​a​d​d​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​
{​
 ​ ​ ​ ​[​_​o​w​n​e​r​N​a​m​e​s​ ​a​d​d​O​b​j​e​c​t​:​n​]​;​
}​

-​ ​(​v​o​i​d​)​r​e​m​o​v​e​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​
{​
 ​ ​ ​ ​[​_​o​w​n​e​r​N​a​m​e​s​ ​r​e​m​o​v​e​O​b​j​e​c​t​:​n​]​;​
}​

-​ ​(​N​S​S​e​t​ ​*​)​o​w​n​e​r​N​a​m​e​s​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​_​o​w​n​e​r​N​a​m​e​s​ ​c​o​p​y​]​;​
}​


@​e​n​d​

Note that this class does not initialize voltage or productName. The initWithProductName: in BNRAppliance takes care of those. When you create a subclass, you typically only need to initialize the instance variables that the subclass introduced; let the superclass take care of the instance variables that it introduced.

Now, however, you face the same situation as you did with BNRAppliance and its superclass’s initializer, init. At the moment, one of your co-workers might create a terrible bug with this line of code:

O​w​n​e​d​A​p​p​l​i​a​n​c​e​ ​*​a​ ​=​ ​[​[​O​w​n​e​d​A​p​p​l​i​a​n​c​e​ ​a​l​l​o​c​]​ ​i​n​i​t​W​i​t​h​P​r​o​d​u​c​t​N​a​m​e​:​@​"​T​o​a​s​t​e​r​"​]​;​

This code will cause the initWithProductName: method in BNRAppliance to run. This method knows nothing about the ownerNames set, which means ownerNames will not get properly initialized for this BNROwnedAppliance instance.

The fix here is the same as before. In BNROwnedAppliance.m, add an implementation of the superclass’s initializer initWithProductName: that calls initWithProductName:firstOwnerName: and passes a default value for firstOwnerName.

-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​P​r​o​d​u​c​t​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​p​n​
{​
 ​ ​ ​ ​r​e​t​u​r​n​ ​[​s​e​l​f​ ​i​n​i​t​W​i​t​h​P​r​o​d​u​c​t​N​a​m​e​:​p​n​ ​f​i​r​s​t​O​w​n​e​r​N​a​m​e​:​n​i​l​]​;​
}​

Quiz time: Do you also need to implement init in BNROwnedAppliance? No. At this point, the following code will work fine:

O​w​n​e​d​A​p​p​l​i​a​n​c​e​ ​*​a​ ​=​ ​[​[​O​w​n​e​d​A​p​p​l​i​a​n​c​e​ ​a​l​l​o​c​]​ ​i​n​i​t​]​;​

Why? There is no implementation of init in BNROwnedAppliance, so this line will trigger the init method implemented in BNRAppliance, which calls [self initWithProductName:@"Unknown"]. self is an instance of BNROwnedAppliance, so it calls initWithProductName: in BNROwnedAppliance, which calls [self initWithProductName:pn firstOwnerName:nil].

What you wind up with is a chain of initializers that call other initializers.

Figure 33.2  Initializer chain

Initializer chain

Notice that Figure 33.2 shows one shaded initializer for each class. This initializer is the designated initializer for that class. init is the designated initializer for NSObject, initWithProductName: is the designated initializer for BNRAppliance, and initWithProductName:firstOwnerName: is the designated initializer for BNROwnedAppliance.

The designated initializer acts as a funnel-point. A class has only one designated initializer method. If the class has other initializers, then the implementation of those initializers must call (directly or indirectly) the designated initializer.

When you create a class whose designated initializer has a different name than its superclass’s designated initializer (as you did in BNRAppliance and BNROwnedAppliance), you have a responsibility to document that in the header file. Add the appropriate comment in BNRAppliance.h:

#​i​m​p​o​r​t​ ​<​F​o​u​n​d​a​t​i​o​n​/​F​o​u​n​d​a​t​i​o​n​.​h​>​

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​A​p​p​l​i​a​n​c​e​ ​:​ ​N​S​O​b​j​e​c​t​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​c​o​p​y​)​ ​N​S​S​t​r​i​n​g​ ​*​p​r​o​d​u​c​t​N​a​m​e​;​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​)​ ​i​n​t​ ​v​o​l​t​a​g​e​;​

/​/​ ​T​h​e​ ​d​e​s​i​g​n​a​t​e​d​ ​i​n​i​t​i​a​l​i​z​e​r​
-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​P​r​o​d​u​c​t​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​p​n​;​

@​e​n​d​

and in BNROwnedAppliance.h:

#​i​m​p​o​r​t​ ​"​B​N​R​A​p​p​l​i​a​n​c​e​.​h​"​

@​i​n​t​e​r​f​a​c​e​ ​B​N​R​O​w​n​e​d​A​p​p​l​i​a​n​c​e​ ​:​ ​B​N​R​A​p​p​l​i​a​n​c​e​

@​p​r​o​p​e​r​t​y​ ​(​r​e​a​d​o​n​l​y​)​ ​N​S​S​e​t​ ​*​o​w​n​e​r​N​a​m​e​s​;​

/​/​ ​T​h​e​ ​d​e​s​i​g​n​a​t​e​d​ ​i​n​i​t​i​a​l​i​z​e​r​
-​ ​(​i​n​s​t​a​n​c​e​t​y​p​e​)​i​n​i​t​W​i​t​h​P​r​o​d​u​c​t​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​p​n​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​i​r​s​t​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​;​
-​ ​(​v​o​i​d​)​a​d​d​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​;​
-​ ​(​v​o​i​d​)​r​e​m​o​v​e​O​w​n​e​r​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​;​

@​e​n​d​

Thus, we arrive at the rules that all stylish Objective-C programmers follow when writing initializers:

  • If a class has several initializers, only one should do the real work. That method is known as the designated initializer. All other initializers should call, either directly or indirectly, the designated initializer.

  • The designated initializer will call the superclass’s designated initializer before initializing its instance variables.

  • If the designated initializer of your class has a different name than the designated initializer of its superclass, you must override the superclass’s designated initializer so that it calls the new designated initializer.

  • If you have several initializers, clearly document which is the designated initializer in the header.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset