Override designated initializer of superclass

2020-06-16 04:04发布

问题:

I am reading a book which has a guideline:

"If a class declares a designated initializer that is different from its superclass, the superclass’s designated initializer must be overridden to call the new designated initializer"

As I understand this guideline in other words is that, if I am subclassing my class form its superclass, and my subclass has a designated initializer which is different from des. initializer of its superclass, then in my subclass I must override the designated initializer of my superclass and inside it call the designated initializer of my subclass.

Is this true? Do we have to do this all the time? Thank you.

回答1:

@justin is basically on the point.

Methods in Objective-C are inherited. That means if the superclass has an initializer method (initializers are just methods), and your subclass does not override it, then your subclass will inherit that superclass's initializer method. That means that people can always call that superclass's initializer on an object of your subclass (basic consequence of inheritance and subtype polymorphism). But that might not be what you expected. The superclass's initializer might not do all the initialization that your class needs.

That's why you should override the superclass's initializer. If you don't want people to use that initializer on an object of your class, you should throw an exception in that initializer. Otherwise, you should override it to do any appropriate initialization for your class.



回答2:

Is this true? Do we have to do this all the time?

Personally, I consider it a bad guideline. It is illogical to implement the superclass' designated initializer (to do anything meaningful) when you have specified a stricter designated initializer (e.g. one which introduces a parameter).

For example, -initWithDomain:code:userInfo: is NSError's designated initializer; Could [[NSError alloc] init] return a reasonably descriptive error?

If anything, privately override the 'deleted' initializer and treat it as an programmer error to call, but do not pretend that it is acceptable for a client to use an initializer other than a designated initializer.

Note that your class will in some cases be able to support both initializers. In that case, just redeclare in your @interface's designated initializers. This is sufficient to document a designated initializer. Either that, or document an initializer or set of initializers as designated initializers, which would logically invalidate any superclass' designated initializers.

Of course, your initializer should call one of the superclass' designated initializers in its initialization.


Ex.1:

// implicitly adds a designated initializer. -init is still valid:
@interface MONObject : NSObject
- (instancetype)initWithString:(NSString *)pString;
@end

Ex.2:

// redefines the designated initializer. -init is not valid:
@interface MONObject : NSObject
// MONObject's designated initializer
- (instancetype)initWithString:(NSString *)pString;
@end

Ex.3:

// define all designated initializers:
@interface MONObject : NSObject
// MONObject's designated initializers:
- (instancetype)init;
- (instancetype)initWithString:(NSString *)pString;
@end

EDIT

Question clarified in comments.

When you are simply overriding an initializer declared by the superclass:

Is this true? Do we have to do this all the time?

Unless your class has initialization to perform, you do not need to explicitly override the superclass' designated initializer.

Your instance will be initialized to have zeroed memory.

Given:

@interface MONObject : NSObject

- (instancetype)initWithString:(NSString *)pString;

@property (nonatomic, copy, readwrite) NSString * string;

@end


@implementation MONObject

// if @property string should be initialized to nil, you may omit -[MONObject init]
// otherwise, initialize self here:
- (instancetype)init
{
 // call super's designated initializer:
 self = [super init];
 // test it:
 if (nil == self) return nil;
 // init your state
 _string = @"(null)";
 return self;
}

- (instancetype)initWithString:(NSString *)pString;
{
 // call super's designated initializer:
 self = [super init]; // << will not call -[MONObject init]
 // test it:
 if (nil == self) return nil;
 // init your state
 _string = pString.copy;
 return self;
}

@end


回答3:

It's basically saying that if a class has an iniWithSomethingDomething, then is preferred to do a

self = [super initWithSomethingSomeThing:......]

in your own initializer



回答4:

I as understand it, If your class has a designated init, you want to override the supers init so it calls your designated init.

in your implementation kind of like so.

make your designated init

-(id) initWithName:(NSString *)aName 
{
    self = [super init];
    if (self){
        [self setName:aName];
    }
    return self;
}

then call it when overriding the supers

-(id) init
{
    return [self initWithName: @""];
}