Почему назначенный инициализатор не может вызвать вторичный инициализатор в своем базовом классе?

Согласно документации, назначенный инициализатор класса в Objective-C должен вызывать назначенный инициализатор своего базового класса.

Другое правило заключается в том, что вторичные инициализаторы должны вызывать назначенный инициализатор своего собственного класса.

Но если соблюдается второе правило, почему назначенный инициализатор не может вызвать вторичный инициализатор в своем базовом классе? Этот базовый вторичный инициализатор в конечном итоге вызовет DI своего собственного уровня, поэтому объект все равно будет правильно инициализирован, верно?

Разница, по-видимому, заключается в том, кто выбирает значения по умолчанию для отсутствующих переменных — вы или ваш базовый класс.


person Halt    schedule 07.04.2012    source источник
comment
У Apple также есть хорошее объяснение здесь: разработчик .apple.com/library/ios/#documentation/Общие/   -  person lnafziger    schedule 07.04.2012


Ответы (2)


Рассмотрим NSSet. Он имеет назначенный инициализатор:

- (id)initWithObjects:(const id *)objects count:(NSUInteger)cnt {
   // initialization code here
   return self;
}

У него также есть несколько вторичных инициализаторов, например этот:

- (id)initWithArray:(NSArray *)array {
    NSUInteger count = array.count;
    id objects[count];
    [array getObjects:objects range:NSMakeRange(0, count)];
    return [self initWithObjects:objects count:count];
}

Теперь вам нужен подкласс NSSet, который автоматически отклоняет строку «Боб». Таким образом, вы должным образом переопределяете назначенный инициализатор в своем подклассе, но вызываете один из вторичных инициализаторов super:

@implementation BobRejectingSet

- (id)initWithObjects:(const id *)objects count:(NSUInteger)count {
    NSMutableArray *array = [[NSMutableArray alloc] initWithCount:count];
    for (NSUInteger i = 0; i < count; ++i) {
        if (![objects[i] isEqual:@"Bob"]) {
            [array addObject:objects[i]];
        }
    }
    return [super initWithArray:array];
}

Что происходит, когда вы делаете это:

BobRejectingSet *noBobs = [[BobRejectingSet alloc] initWithArray:someObjects];

Поскольку вы не переопределили initWithArray:, программа вызывает -[NSSet initWithArray:], который вызывает назначенный инициализатор initWithObjects:count:. Вы переопределили назначенный инициализатор, поэтому он вызывает ваш метод. Ваш метод отфильтровывает Bobs, а затем вызывает вторичный инициализатор super, initWithArray:…, который разворачивается и снова вызывает указанный вами переопределенный инициализатор. Неограниченная рекурсия. Переполнение стека. Вы получаете блюз нарушения сегментации ядра.

Вот почему вы всегда используете назначенный супер инициализатор.

person rob mayoff    schedule 07.04.2012
comment
Предположительно, это происходит только в том случае, если ваш init называется так же, как один из init суперкласса. Если бы вы назвали это initWithoutBob:, вы были бы в порядке, верно? - person Robert; 22.08.2016
comment
Тогда это не будет назначенный инициализатор. - person rob mayoff; 22.08.2016

Если бы назначенный инициализатор вызывал вторичный инициализатор в своем собственном классе, то он не выполнял бы роль назначенного инициализатора. Смысл назначенного инициализатора в том, чтобы разработчик подкласса знал единственный инициализатор, который он/она может переопределить, через который будут проходить все остальные инициализаторы.

Если назначенный инициализатор в подклассе вызывает неназначенный инициализатор в своем суперклассе, то инициализатор суперкласса, скорее всего, развернется и вызовет другой из инициализаторов. Из-за динамического характера диспетчеризации методов он не может ограничиваться вызовом только методов, реализованных на его уровне. Этот вызов, скорее всего, будет одним из переопределений подкласса, который будет направлен в назначенный инициализатор подкласса, что приведет к бесконечной рекурсии.

person Ken Thomases    schedule 07.04.2012