← Назад к вопросам

Что должен вызвать designated?

1.8 Middle🔥 211 комментариев
#Архитектура и паттерны#Язык Swift

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Основная роль Designated Initializer

В контексте Objective-C (и унаследованных принципов в Swift) designated initializer («назначенный инициализатор») — это главный, полноценный инициализатор класса, который должен полностью инициализировать все свойства, унаследованные от суперкласса, а также собственные свойства текущего класса.

Что он обязан вызвать?

Designated initializer должен вызвать designated initializer своего непосредственного суперкласса (родительского класса). Это фундаментальное правило наследования в Objective-C и одна из ключевых гарантий корректной инициализации объекта по цепочке наследования.

В Objective-C это выглядит так:

@interface MySubclass : MySuperclass
@property (nonatomic, strong) NSString *subclassProperty;
@end

@implementation MySubclass

// Designated Initializer подкласса
- (instancetype)initWithValue:(NSString *)value {
    // Шаг 1: Вызов designated initializer суперкласса
    self = [super init]; // Где 'init' - это designated initializer MySuperclass
    if (self) {
        // Шаг 2: Инициализация собственных свойств подкласса
        _subclassProperty = [value copy];
    }
    return self;
}

@end

В Swift правило трансформируется, но суть остаётся:

  • В Swift явного разделения на designated и convenience нет, но есть назначенные инициализаторы (просто init) и вспомогательные (convenience init).
  • Назначенный инициализатор подкласса должен сначала вызвать назначенный инициализатор своего непосредственного суперкласса, а уже потом устанавливать значения унаследованных свойств (которые можно изменить) и своих собственных.
class MySuperclass {
    let superValue: Int
    // Назначенный инициализатор суперкласса
    init(superValue: Int) {
        self.superValue = superValue
    }
}

class MySubclass: MySuperclass {
    let subclassValue: String

    // Назначенный инициализатор подкласса
    init(subclassValue: String) {
        // Шаг 1: Инициализация всех свойств, введенных подклассом
        self.subclassValue = subclassValue
        // Шаг 2: Вызов назначенного инициализатора суперкласса
        super.init(superValue: 0) // Обязательный вызов!
        // Шаг 3: Дополнительная настройка унаследованных свойств (при необходимости)
        // self.someInheritedProperty = ...
    }
}

Почему это так важно? Последствия нарушения

  1. Неполная инициализация. Если designated initializer не вызовет [super init...], цепочка инициализации прервется. Суперкласс не выполнит свою критически важную работу: выделение памяти, настройку внутренних структур (например, isa-указатель в Objective-C), инициализацию своих свойств. Объект останется в нестабильном, потенциально нефункциональном состоянии.

  2. Нарушение контракта наследования. Класс-потомок расширяет функциональность родителя, а не заменяет её. Вызов инициализатора суперкласса — это соблюдение этого контракта, гарантия того, что базовая часть объекта готова к работе.

  3. Краш приложения. Невыполнение этого требования в Objective-C часто приводит к непредсказуемому поведению и крашам (например, EXC_BAD_ACCESS при обращении к неинициализированной памяти). Swift компилятор строже контролирует этот процесс на этапе компиляции, делая многие ошибки невозможными.

Контекст и дополнительные правила (Objective-C)

  • Convenience (вспомогательные) инициализаторы НЕ являются designated. Их задача — предоставить альтернативный, более удобный интерфейс для создания объекта. Они должны в конечном итоге вызвать designated initializer своего же класса (не суперкласса!).
    - (instancetype)initWithDefaultValue {
        // Convenience инициализатор вызывает designated своего класса
        return [self initWithValue:@"Default"];
    }
    
  • Класс может иметь несколько designated initializer'ов, но это считается плохой практикой и усложняет наследование. Канонический подход — один primary designated initializer.
  • В документации Apple (и в коде) часто можно встретить пометку NS_DESIGNATED_INITIALIZER, которая явно указывает компилятору и разработчику, какой метод является назначенным.

Итог

Designated initializer обязан вызвать designated initializer своего непосредственного суперкласса ([super init...]). Это не рекомендация, а обязательное правило, обеспечивающее корректность, безопасность и предсказуемость процесса создания объектно-ориентированной иерархии. Пренебрежение им ведет к фундаментальным ошибкам, которые сложно отлаживать. Swift, унаследовав эту концепцию, сделал её более безопасной за счёт строгой проверки на этапе компиляции.