Инициализация класса с помощью инициализатора суперкласса

у меня есть два класса, один подкласс другого (скажем 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. Например, вы можете рассмотреть вопрос о создании различных объектов корневого уровня животных и просто иметь протокол животных, требующий, чтобы все различные "животные" отвечали на определенные сообщения животных. Таким образом, каждое животное (и истинные подклассы животных) может обрабатывать свои назначенные инициализаторы самостоятельно и не будет полагаться на суперклассы, ведущие себя в таком конкретном, нестандартная манера.