Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

objective-c - calling one constructor from another

Tags:

objective-c

Say you had the following two constructors:

- (id)initWithTitle:(NSString *)title;
- (id)initWithTitle:(NSString *)title page:(NSString *)page;

The second constructor is no different from the first, except that it sets up the member variable "page".

Since it basically has to do the same thing, is there a way to call the first one from the second one to reduce code duplication, or do you have to set up a third method to do the common tasks?

I'm talking about something similar to this, though I doubt this will work:

- (id)initWithTitle:(NSString *)_title {
    if(self = [super init]) {
        self.title = _title;
    }

    return self;
}

- (id)initWithTitle:(NSString *)_title page:(NSString *)_page {
     if(self = [self initWithTitle:_title]) {
         self.page = _page;
     }

     return self;
}
like image 319
synic Avatar asked May 15 '10 14:05

synic


2 Answers

What you are saying is weird because I'm thinking of this

- (id)initWithTitle:(NSString *)_title {
    return [self initWithTitle:_title page:nil];
}

- (id)initWithTitle:(NSString *)_title page:(NSString *)_page {
    if(self = [super init]) {
        self.title = _title;
        self.page = _page;
    }
    return self;
}

Doesn't it work?

like image 197
phunehehe Avatar answered Sep 30 '22 00:09

phunehehe


First, you don't have constructors, you have two initializers:

- (id)initWithTitle:(NSString *)title;
- (id)initWithTitle:(NSString *)title page:(NSString *)page;

As with most initializers, the goal is to setup some instance variables (not member variables).

The first step is to identify your designated initializer. In your case, it could be:

- (id)initWithTitle:(NSString *)title page:(NSString *)page;

Which means that - (id)initWithTitle:(NSString *)title; would be implemented as others have described:

- (id)initWithTitle:(NSString *)_title {
    return [self initWithTitle:_title page:nil];
}

However, I would recommend against this pattern as it makes subclassing more error prone than it should be. In subclassing, you must always override the designated initializer or you must implement a new initializer that calls the designated initializer. Obviously, the more initializers you have, the more potential for bugs in subclassing you might introduce.

Furthermore, this:

Foo *f = [[Foo alloc] initWithTitle: @"Foo!" page: nil];

Is far more clear than this:

Foo *f = [[Foo alloc] initWithTitle: @"Foo!"];

The first indicates quite specifically that you have thought about page and decided to explicitly set it to nil or otherwise rely on the class Foo to set the page reasonably. The second gives no such indication.

Both subclassing and explicit indication of intention are the primary reasons why you will find a lack of such convenience methods across Cocoa (there are some, but most are quite old -- older than when the policy really became rule).

like image 21
bbum Avatar answered Sep 30 '22 00:09

bbum