2008-11-10 195 views
7

我有两个类,一个是另一个的子类(说AnimalDog)。超类有一些初始化(例如initAnimal),子类有一些初始化(比如initDog)。问题在于,它完全合法(从编译器的角度来看)做类似Dog *adog = [[Dog alloc] initAnimal]的事情,即。使用其超类初始化器初始化一个类。我不喜欢这样,因为子类可以有一些额外的实例变量,我想确保它们被初始化。看看头文件解决了这个问题,但有没有简单的方法来让我的编译器检查?我有一种感觉,我失去了一些东西非常明显的,但我不能把我的手指上:-)使用超类初始化程序初始化一个类

更新:initDoginitAnimal不是最好的例子。我的意思是两种非常不同的初始化程序(如init对于AnimalinitWithFur对于Dog)。如果我想让每只狗都分配一些皮毛,我会将皮毛制成初始化工具的一部分,这样就没有人可以在没有皮毛的情况下获得一个狗物体。但是,仍然很容易错误地使用超类init初始化实例,然后我洗脑。

感谢您提出指定的初始值设定项,Jason。它以前没有发生过,但我可以重载超类的指定初始化程序,并在那里设置一些正常的默认值。但是,如果我能以某种方式使用其他初始化程序而非类本身的初始化程序是非法的 - 我还会更喜欢 - 还有什么想法?

+0

好Zoul,我所做的更新基于更新你的问题我的答案......这是相当长的了,但我希望它给你一些你要找的洞察力。 – 2008-11-11 08:24:36

+0

因为这是最好的结果。现代XCode中的答案可以通过NS_UNAVAILABLE实现。请参阅:http://stackoverflow.com/questions/195078/is-it-possible-to-make-the-init-method-private-in-objective-c#answer-27693034 – m4js7er 2016-03-17 16:20:57

回答

18

通常在Objective-C中为每个类创建一个指定的初始化程序,然后子类使用相同的初始化程序。因此,不是使用initAnimal和initDog,而是使用init。然后狗子类会定义自己的init方法,并调用指定的初始化其父类:

@implementation Dog 
-(id)init 
{ 
    if((self = [super init])) { // call init in Animal and assign to self 
     // do something specific to a dog 
    } 
    return self; 
} 
@end 

你真的没有指定initDog和initAnimal因为类是对的右侧声明分配...

更新:我加入了以下的答案,以反映问题的其他信息

有许多的方法,以确保子不叫比其他初始化他们指定的初始化程序和你最终的方式ely选择将主要基于您的整个设计。 Objective-C的好处之一是它非常灵活。我会在这里给你两个例子让你开始。首先,如果您创建的子类具有与其父类不同的指定初始值设定项,您可以重载父项的初始值设定项并引发异常。这会让程序员立即知道他们已经违反了班级的协议......但是,应该说你应该有一个很好的理由来做这件事,并且应该有很好的文件证明子类可能会不要使用与超类相同的初始值设定项。

@implementation Dog 
-(id)init 
{ 
    // Dog does not respond to this initializer 
    NSAssert(false, @"Dog classes must use one of the designated initializers; see the documentation for more information."); 

    [self autorelease]; 
    return nil; 
} 

-(id)initWithFur:(FurOptionsType)furOptions 
{ 
    if((self = [super init])) { 
     // do stuff and set up the fur based on the options 
    } 
    return self; 
} 
@end 

另一种方法是让初始化器更像你的原始示例。在这种情况下,您可以更改父类的默认初始值以始终失败。然后您可以为您的父类创建一个私有初始化器,然后确保每个人都在子类中调用适当的初始化器。这种情况显然比较复杂:

@interface Animal : NSObject 
-(id)initAnimal; 
@end 

@interface Animal() 
-(id)_prvInitAnimal; 
@end 

@interface Dog : Animal 
-(id)initDog; 
@end 

@implementation Animal 
-(id)init 
{ 
    NSAssert(false, @"Objects must call designated initializers; see documentation for details."); 

    [self autorelease]; 
    return nil; 
} 

-(id)initAnimal 
{ 
    NSAssert([self isMemberOfClass:[Animal class]], @"Only Animal may call initAnimal"); 

    // core animal initialization done in private initializer 
    return [self _prvInitAnimal]; 
} 

-(id)_prvInitAnimal 
{ 
    if((self = [super init])) { 
     // do standard animal initialization 
    } 
    return self; 
} 
@end 

@implementation Dog 
-(id)initDog 
{ 
    if((self = [super _prvInitAnimal])) { 
     // do some dog related stuff 
    } 
    return self; 
} 
@end 

在这里您会看到Animal and Dog类的接口和实现。 Animal是指定的顶层对象,因此覆盖NSObject的init实现。任何在动物或动物的任何子类上调用init的人都会得到一个断言错误,将其引用到文档中。 Animal还定义了私人类别上的私人初始值设定项。私有类将留在你的代码中,当它们调用super时,Animal的子类将调用这个私有的初始化方法。它的目的是调用Animal的超类(在这种情况下为NSObject)上的init,并执行可能必需的任何通用初始化。

最后,在动物的initAnimal方法的第一行是断言,接收器实际上是一个动物,而不是一些子类。如果接收器不是动物,则程序将失败并出现断言错误,程序员将被引用到文档中。

这些只是你如何设计一些符合你的特定要求的两个例子。但是,我会强烈建议你考虑你的设计约束,看看你是否真的需要这种设计,因为它在可可和大多数OO设计框架中都是非标准的。例如,你可以考虑制作各种动物根级对象,而只需要动物协议,要求所有各种“动物”对某些动物通用消息作出响应。这样,每只动物(和动物的真正的亚类)都可以自己处理它们的指定初始化子,并且不必依赖以这种特定的非标准方式行事的超类。