В чем разница между модификаторами convenience и required у инициализаторов в Swift?

«В чем разница между модификаторами convenience и required у инициализаторов в Swift?» — вопрос из категории Swift Core, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

convenience — обозначает вспомогательный (вторичный) инициализатор, который должен внутри вызвать designated (назначенный) инициализатор того же класса. Упрощает создание объекта с альтернативными параметрами или значениями по умолчанию.

required — обязывает все прямые подклассы реализовать данный инициализатор. Гарантирует, что определённая логика инициализации будет доступна во всей иерархии наследования.

Основные отличия:

convenience required
Назначение Упрощение инициализации Контракт для подклассов
Вызов Должен вызвать self.init Может быть как designated, так и convenience
Наследование Может быть переопределён в подклассе Обязателен для реализации в подклассе
Использование Альтернативные параметры, значения по умолчанию Протоколы, классовые типы, фабричные методы

Пример convenience инициализатора:

class Document {
    var title: String
    var body: String

    // Designated инициализатор
    init(title: String, body: String) {
        self.title = title
        self.body = body
    }

    // Convenience инициализатор
    convenience init(title: String) {
        self.init(title: title, body: "") // Вызов designated
    }

    // Ещё один convenience
    convenience init() {
        self.init(title: "Untitled") // Вызов другого convenience
    }
}

let doc1 = Document(title: "Report", body: "Content") // Designated
let doc2 = Document(title: "Note") // Convenience
let doc3 = Document() // Convenience -> Convenience -> Designated

Пример required инициализатора:

class Vehicle {
    let wheels: Int

    // Все подклассы должны реализовать этот init
    required init(wheels: Int) {
        self.wheels = wheels
    }
}

class Car: Vehicle {
    let brand: String

    init(brand: String) {
        self.brand = brand
        super.init(wheels: 4)
    }

    // Обязательная реализация required инициализатора
    required init(wheels: Int) {
        self.brand = "Unknown"
        super.init(wheels: wheels)
    }
}

class Bicycle: Vehicle {
    // Тоже должен реализовать required init
    required init(wheels: Int) {
        super.init(wheels: wheels)
    }
}

// Использование с классовой переменной (метаклассом)
func createVehicle(ofType type: Vehicle.Type) -> Vehicle {
    return type.init(wheels: 2) // Требуется required init
}

let bike = createVehicle(ofType: Bicycle.self)

Важно:

  • convenience инициализатор не может вызвать другой convenience инициализатор суперкласса, только designated.
  • required модификатор часто сочетается с convenience, если нужен фабричный метод в иерархии.
  • При переопределении required инициализатора в подклассе также нужно указывать required (а не override).