Инициализация класса с помощью инициализатора суперкласса
у меня есть два класса, один подкласс другого (скажем Animal
и Dog
). У суперкласса есть некоторые инициализаторы (скажем initAnimal
), подкласс имеет некоторые инициализаторы (скажем initDog
). Проблема в том, что совершенно законно (с точки зрения компилятора) делать что-то вроде Dog *adog = [[Dog alloc] initAnimal]
, ie. инициализируйте класс, используя его инициализатор суперкласса. Мне это не нравится, потому что подкласс может иметь некоторые дополнительные переменные экземпляра, которые я хочу убедиться, инициализированы. Взгляд в файл заголовка решает это, но есть ли простой способ сделать проверку компилятора для меня? У меня такое чувство, что мне не хватает чего-то ужасно очевидного, но я просто не могу на это указать: -)
обновление: на initDog
и initAnimal
не были лучшими примерами. Я имел в виду два действительно разных инициализатора (например,init
на Animal
и initWithFur
на Dog
). Если бы я хотел, чтобы у каждой собаки был какой-то мех, я бы сделал меховую часть инициализатора, так что никто не мог получить предмет собаки без меха. Но тогда по-прежнему легко ошибочно инициализировать экземпляр с помощью суперкласса init
и тогда я понял.
Спасибо, что поднял назначенные инициализаторы, Джейсон. Раньше мне это не приходило в голову, но я мог перегрузить назначенный инициализатор суперкласса и установить там некоторые разумные значения по умолчанию. Но я все равно предпочел бы, чтобы я мог каким – то образом сделать незаконным использование других инициализаторов, чем те из самого класса-больше идеи?
1 ответов
обычно в 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
другой способ сделать это, чтобы иметь инициализатор больше похож на оригинальный образец. В этом случае вы можете изменить инициализацию по умолчанию в родительском классе на всегда сбой. Затем вы можете создать частный инициализатор для родительского класса, а затем убедиться, что каждый вызывает соответствующий инициализатор в подклассах. Этот случай, очевидно, более сложный:
@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
здесь вы видите интерфейс и реализацию класса животных и собак. Животное является назначенным объектом верхнего уровня и поэтому переопределяет объект NSObject внедрение init. Любой, кто вызывает init на животное или любой из подклассов животного, получит ошибку утверждения, ссылаясь на документацию. Animal также определяет частный инициализатор в частной категории. Частная категория останется с вашим кодом, а подклассы Animal вызовут этот частный инициализатор, когда они вызовут super. Его цель-вызвать init на суперклассе Animal (в этом случае NSObject) и сделать любую общую инициализацию, которая может быть необходимый.
наконец, первая строка в initAnimal методе животного-это утверждение, что получатель на самом деле животное, а не какой-то подкласс. Если получатель не является животным, программа завершится с ошибкой утверждения, и программист будет передан в документацию.
Это всего лишь два примера того, как вы можете проектировать что-то с вашими конкретными требованиями. Тем не менее, я бы сильно предлагаю вам рассмотреть ваши конструктивные ограничения и посмотрите, действительно ли вам нужен этот тип дизайна, поскольку он нестандартен в Cocoa и в большинстве структур дизайна OO. Например, вы можете рассмотреть вопрос о создании различных объектов корневого уровня животных и просто иметь протокол животных, требующий, чтобы все различные "животные" отвечали на определенные сообщения животных. Таким образом, каждое животное (и истинные подклассы животных) может обрабатывать свои назначенные инициализаторы самостоятельно и не будет полагаться на суперклассы, ведущие себя в таком конкретном, нестандартная манера.