覆盖超类的指定初始化方法

9

我正在阅读一本关于指南:

“如果一个类声明了一个与其超类不同的指定初始化器,则必须重写超类的指定初始化器以调用新的指定初始化器”

我的理解是,如果我从超类中继承了一个子类,并且我的子类具有与其超类不同的指定初始化器,那么我在子类中必须重写超类的指定初始化器,并在其中调用子类的指定初始化器。

这是真的吗?我们必须一直这样做吗?谢谢。

4个回答

13

@justin说得基本正确。

在 Objective-C 中,方法是可以被继承的。这意味着如果超类有一个初始化方法 (初始化器只是一种方法),而你的子类没有覆盖它,则你的子类将继承该超类的初始化方法。这意味着人们总是可以在你的子类对象上调用超类的初始化方法 (继承和子类型多态的基本后果)。但这可能不是你预期的结果。超类的初始化方法可能无法完成你的类所需的所有初始化工作。

这就是为什么你应该重写超类的初始化方法。如果你不希望人们在你的类上使用该初始化方法,那么你应该在该初始化方法中抛出异常。否则,你应该重写它来为你的类做任何适当的初始化工作。


2
“这是真的吗?我们需要一直这样做吗?”
个人认为这是一个不好的指南。如果您指定了更严格的指定初始化程序(例如引入参数的初始化程序),在实现超类的指定初始化程序时(要做任何有意义的事情)是不合逻辑的。
例如,“-initWithDomain:code:userInfo:”是“NSError”的指定初始化程序;[[NSError alloc] init]可以返回一个相当描述性的错误吗?
如果有什么问题,私下重写“deleted”初始化程序,并将其视为程序员错误来调用,但不要假装客户端可以使用指定初始化程序以外的初始化程序。
请注意,您的类在某些情况下可能能够支持两种初始值设定程序。在这种情况下,在您的界面中重新声明指定的初始化程序即可。这足以记录指定的初始化程序。或者,将初始化程序或初始化程序集合的一组初始化程序记录为指定的初始化程序,这会逻辑上使任何超类的指定初始化程序无效。
当然,您的初始化程序应在其初始化中调用超类的指定初始化程序。
示例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

编辑

在评论中澄清问题。

当您只是覆盖超类声明的初始化程序时:

这是真的吗?我们必须一直这样做吗?

除非您的类有初始化要执行,否则不需要显式地覆盖超类的指定初始化程序。

您的实例将被初始化为具有零化内存。

给定:

@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

Justin,我的问题更多地与这个有关:http://stackoverflow.com/questions/5753038/why-override-the-designated-initializer-of-super-class?rq=1,你可以在那里看到代码,继承的指定初始化程序是如何被覆盖的 - 这就是我的问题所涉及的地方。只是我不确定那里的答案是否足够描述。谢谢。 - user2568508
亲爱的贾斯汀,麻烦您看一下我给乔·米隆提供的链接,最终澄清我的意思。 - user2568508
@user2568508 当你需要初始化你的类(例如它的实例变量)时,你可以重写超类的初始化程序。默认行为通常是可接受的。当默认行为可接受时,不需要提供覆盖。Caleb很好地回答了相关问题。 - justin

0

这基本上是说,如果一个类有一个 iniWithSomethingDomething,那么最好做一个

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

在你自己的初始化器中


Rob,不是那样的,请注意:“超类的指定初始化程序必须被覆盖以调用新的指定初始化程序”。 - user2568508
我现在开始理解这个问题了。当类A使用initA,而你的子类使用initB时,最好重载initA来使用initB。听起来很合理,因为用户可能不知道在调用初始化器时正在使用一个子类。 - Rob van der Veer

0
据我所理解,如果你的类有一个指定的init,你想要重写超类的init,以便它调用你的指定init。
在你的实现中,就像这样。
创建你的指定init。
-(id) initWithName:(NSString *)aName 
{
    self = [super init];
    if (self){
        [self setName:aName];
    }
    return self;
}

然后在覆盖父类时调用它

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

是的,如果我的类有一个指定的初始化方法与我的超类的指定初始化方法不同,那么我必须在我的类中重写超类的指定初始化方法。这是指南,我只是想知道为什么...例如,在这里看到:http://forums.bignerdranch.com/viewtopic.php?f=213&t=4180,JoeConway的回复正好回答了我的问题。 - user2568508
你的类可能有一些实例变量需要在可能的情况下进行设置,即使已经调用了常规的初始化方法。比如说你有一个NSDate对象,表示对象创建的日期。你可以在initWithName:方法中设置它,在使用init方法时仍然会被调用。如果你需要在初始化对象时传递参数,那么就会阻止init方法的正常工作。 - Joe Million

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接