2008-11-10 141 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設計框架中都是非標準的。例如,你可以考慮製作各種動物根級對象,而只需要動物協議,要求所有各種「動物」對某些動物通用消息作出響應。這樣,每隻動物(和動物的真正的亞類)都可以自己處理它們的指定初始化子,並且不必依賴以這種特定的非標準方式行事的超類。