通常、Objective-C では、クラスごとに指定された初期化子を作成し、サブクラスは同じ初期化子を使用します。したがって、initAnimal と initDog を使用する代わりに、init を使用するだけです。次に、dog サブクラスは独自の 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 を指定する必要はありません...
更新:質問の追加情報を反映するために、回答に以下を追加しています
サブクラスが指定された初期化子以外の初期化子を呼び出さないようにする方法はいくつかありますが、最終的に選択する方法は主に設計全体に基づいています。Objective-C の優れている点の 1 つは、柔軟性が高いことです。ここでは、まず 2 つの例を示します。
まず、親クラスとは異なる指定イニシャライザを持つサブクラスを作成すると、親のイニシャライザをオーバーロードして例外をスローできます。これにより、プログラマーはクラスのプロトコルに違反していることをすぐに知ることができます...ただし、これを行うには十分な理由が必要であり、サブクラスが使用しない可能性があることを十分に文書化する必要があることを述べておく必要がありますスーパークラスと同じ初期化子。
@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
それを行う別の方法は、初期化子を元の例のようにすることです。その場合、親クラスのデフォルトの init を変更して、常に失敗するようにすることができます。次に、親クラスのプライベート イニシャライザを作成し、すべてのユーザがサブクラスで適切なイニシャライザを呼び出すようにします。このケースは明らかにもっと複雑です:
@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 および Dog クラスのインターフェースと実装を確認できます。Animal は指定された最上位オブジェクトであるため、NSObject の init の実装をオーバーライドします。アニマルまたはアニマルのサブクラスのいずれかで init を呼び出すと、ドキュメントを参照してアサーション エラーが発生します。Animal は、プライベート カテゴリのプライベート イニシャライザも定義します。private カテゴリはコードに残り、Animal のサブクラスは super を呼び出すときにこの private 初期化子を呼び出します。その目的は、Animal のスーパークラス (この場合は NSObject) で init を呼び出し、必要な一般的な初期化を行うことです。
最後に、Animal の initAnimal メソッドの最初の行は、レシーバーが実際には Animal であり、サブクラスではないという主張です。レシーバーが Animal でない場合、プログラムはアサーション エラーで失敗し、プログラマーにドキュメントを参照してもらいます。
これらは、特定の要件で何かを設計する方法の 2 つの例にすぎません。ただし、Cocoa およびほとんどの OO 設計フレームワークでは非標準であるため、設計上の制約を考慮し、このタイプの設計が本当に必要かどうかを確認することを強くお勧めします。たとえば、さまざまな動物のルート レベル オブジェクトを作成することを検討し、代わりに Animal プロトコルを使用して、さまざまな「動物」のすべてが特定の動物ジェネリック メッセージに応答することを要求することができます。そうすれば、各アニマル (およびアニマルの真のサブクラス) は、指定されたイニシャライザ自体を処理でき、そのような特定の非標準的な方法で動作するスーパークラスに依存する必要がなくなります。