Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Designated initializer and convenience initializer in objective-c and how to properly create them and tie them together

I am trying to understand which should be designated initializer and which one convenience initializer. I read apple docs on that topic but i am still not sure. Does the designated initializer has to have all the values that requires for the class? For example
This was the first designated initializer that i created

-(id)initWithCardCount:(NSUInteger)cardCount usingDeck:(Deck *)deck
{
    self = [super init];
    if (self) {
        for(int i = 0; i<=cardCount;i++){
            Card *card = [deck drawRandomCard];
            if (!card) {
                self = nil;
                break;
            }else{
                self.cards[i] = card;
            }
        }
    }

    return self;
}

Now adde cardMatchMode property to this class and would like to set it in the initializer. To make the class backward compatible and to understand initializers i am keeping the one that i have now and creating another initializer.

-(id)initwithCardCount:(NSUInteger)cardCount usingDeck:(Deck *)deck cardMatchMode:(NSUInteger)matchMode
{

    _cardMatchMode = matchMode;
    return [self initWithCardCount:cardCount usingDeck:deck];;
}

Based on the apple docs the convenience initializer has to return the value for the designated initializer but the question is can i set the extra property on this class in the convenience initializer? Can i say self.cardMatchMode = matchMode; or self is still not fully initialized?
It does work but i just wanted to understand if this is proper code and i can access the cardMatchMode property in the convenience init or i would have to make -(id)initwithCardCount:(NSUInteger)cardCount usingDeck:(Deck *)deck cardMatchMode:(NSUInteger)matchMode

as the designated initializer and the other one convenience init and rework the code? Thanks!

/////Update

I was getting an error in the

-(id)initwithCardCount:(NSUInteger)cardCount usingDeck:(Deck *)deck cardMatchMode:(NSUInteger)matchMode

When i tried to do self = [self initWithCardCount:(NSUInteger)cardCount usingDeck:(Deck*)deck; the error said that you cannot assign self outside the init family. I figured it out what was the problem. The init method had lowercase w and it had to be capital so now it works. This is the code i have now for my convenience initializer.

-(id)initWithCardCount:(NSUInteger)cardCount usingDeck:(Deck *)deck cardMatchMode:(NSUInteger)matchMode
{
    self = [self initWithCardCount:cardCount usingDeck:deck];
    if (self){
        _cardMatchMode = matchMode;
    }
    return self;
}

Now that makes more sense. I called the designated init which calls super and then i set the cardMatchMode variable.

As far as i understand there are a lot of objects that have convenience initializer with extra parameters and it will just call's designated init. If you look at NSString and it has different initializers with different parameters. It's probably calling init which is the designated initializer. Is this correct?

like image 522
Yan Avatar asked Aug 07 '13 19:08

Yan


People also ask

What is the difference between convenience and designated initializer?

Convenience initializers are also aptly named, and they are initializers used to make initialization a bit easier. Designated initializers tend to set all of the properties up and let the user send in values for each. A convenience initializer often has some of those hard coded, and thus can take less parameters.

Why do we need convenience initializer of the same task can be obtained with designated initializer?

A convenience initializer is a secondary initializer that must call a designated initializer of the same class. It is useful when you want to provide default values or other custom setup. A class does not require convenience initializers.

What is a designated initializer?

A designated initializer, or designator, points out a particular element to be initialized. A designator list is a comma-separated list of one or more designators. A designator list followed by an equal sign constitutes a designation.

What are convenience and required Initializers in Swift explain briefly about them?

Convenience initializers are secondary, supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer's parameters set to default values.


2 Answers

The designated initializer is the one that invokes the designated initializer of its class's superclass, so it is the only initializer that invokes a method on the object super. No other initializer in the class should do that; these secondary initializers should invoke the designated initializer using self. Typically the designated initializer has all the parameters necessary to create a useful object of the class.

In short, the designated initializer is the only one that invokes an initializer on super, and all other initializers in the class invoke the designated initializer (using self).

like image 62
Fred Avatar answered Nov 08 '22 18:11

Fred


According to Apple Documentation, the designated initalizer

is typically the init... method that has the most parameters and that does most of the initialization work.

So in your case this will be - (id)initwithCardCount:(NSUInteger)cardCount usingDeck:(Deck *)deck cardMatchMode:(NSUInteger)matchMode. All other initializers will have less arguments, filling lacking parameters on the designated initializer with default or otherwise inferred values.

So each convenience initializer calls designated initializer instead of superclass's initializer.

Convenience-designated initializer pair will look like this in your case:

/**
 Convenience initializers
*/
- (id)init
{
    self = [self initwithCardCount:kDefaultCardCount usingDeck:[Deck defaultDeck] cardMatchMode:kCardMatchModeDefault];
    return self;
}

- (id)initWithCardCount:(NSUInteger)cardCount usingDeck:(Deck *)deck
{
    self = [self initwithCardCount:cardCount usingDeck:deck cardMatchMode:kCardMatchModeDefault]; 
    if (self) {

    }

    return self;
}

/**
 Designated initializer
*/
- (id)initwithCardCount:(NSUInteger)cardCount usingDeck:(Deck *)deck cardMatchMode:(NSUInteger)matchMode
{
    self = [super init];
    if (self) {
        for (int i = 0; i <= cardCount; i++) {
            Card *card = [deck drawRandomCard];
            if (!card) {
                self = nil;
                break;
            }
            else {
                self.cards[i] = card;
            }
        }
        // Set card match mode here:
        _cardMatchMode = matchMode;
    }

    return self;
}
like image 39
ivanmoskalev Avatar answered Nov 08 '22 19:11

ivanmoskalev