Обычно в 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 в том, что она такая гибкая. Я приведу два примера, чтобы вы могли начать.
Во-первых, если вы создаете подкласс, который имеет другой назначенный инициализатор, чем его родительский класс, вы можете перегрузить инициализатор родителя и выдать исключение. Это немедленно сообщит программистам, что они нарушили протокол для вашего класса ... однако, следует указать, что у вас должна быть очень веская причина для этого и что это должно быть очень хорошо задокументировано что подкласс не может использовать тот же инициализатор, что и суперкласс.
@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 является обозначенным объектом верхнего уровня и поэтому переопределяет реализацию init в NSObject. Любой, кто вызовет init для Animal или любого из подклассов Animal, получит ошибку подтверждения, ссылаясь на документацию. Animal также определяет частный инициализатор для частной категории. Закрытая категория останется с вашим кодом, а подклассы Animal будут вызывать этот закрытый инициализатор при вызове super. Его целью является вызов init для суперкласса Animal (в данном случае NSObject) и выполнение любой общей инициализации, которая может потребоваться.
Наконец, первая строка в методе Animal initAnimal - это утверждение, что получатель на самом деле является Animal, а не каким-то подклассом. Если получатель не является животным, программа завершится с ошибкой подтверждения, и программист будет обращаться к документации.
Это всего лишь два примера того, как вы можете спроектировать что-то с учетом ваших конкретных требований. Тем не менее, я бы настоятельно предложил бы вам рассмотреть ограничения вашего дизайна и посмотреть, действительно ли вам нужен этот тип дизайна, поскольку он нестандартен в Какао и в большинстве сред разработки OO. Например, вы можете рассмотреть возможность создания различных корневых объектов для животных и использовать вместо них протокол Animal, требующий, чтобы все различные «животные» отвечали на определенные сообщения, характерные для животных. Таким образом, каждое животное (и настоящие подклассы Animal) может самостоятельно обрабатывать назначенные им инициализаторы, и им не придется полагаться на суперклассы, ведущие себя таким специфическим, нестандартным образом.