Ответ
Инициализаторы в Swift делятся на два четких типа, образующих иерархию делегирования для гарантии полной инициализации всех свойств.
Designated (Назначенные) Инициализаторы
- Основная роль: Полностью инициализируют все свойства, введенные классом, и вызывают инициализатор суперкласса (для классов), чтобы продолжить цепочку инициализации вверх.
- Делегирование: Могут вызывать только designated инициализаторы своего непосредственного суперкласса.
- Количество: Класс должен иметь как минимум один designated инициализатор.
Convenience (Вспомогательные) Инициализаторы
- Основная роль: Предоставляют альтернативные, более удобные способы создания экземпляра. Они не обязаны полностью инициализировать свойства самостоятельно.
- Делегирование: Должны в конечном счете вызвать designated инициализатор того же класса (не суперкласса!).
- Цель: Установка значений по умолчанию, преобразование типов, предоставление более ясного API.
Правила делегирования
- Designated → Superclass: Designated инициализатор должен вызвать designated инициализатор суперкласса (в классах).
- Convenience → Self: Convenience инициализатор должен вызвать другой инициализатор того же класса.
- Convenience → Designated: Цепочка вызовов convenience инициализаторов должна в итоге привести к вызову designated инициализатора того же класса.
Пример иерархии
class Food {
var name: String
// DESIGNATED инициализатор
init(name: String) {
self.name = name
}
// CONVENIENCE инициализатор
convenience init() {
self.init(name: "[Unnamed]") // Вызывает designated своего класса
}
}
class RecipeIngredient: Food {
var quantity: Int
// DESIGNATED инициализатор подкласса
init(name: String, quantity: Int) {
self.quantity = quantity // 1. Инициализируем свои свойства
super.init(name: name) // 2. Вызываем designated суперкласса
}
// CONVENIENCE инициализатор (переопределяет convenience суперкласса)
override convenience init(name: String) {
self.init(name: name, quantity: 1) // Вызывает designated СВОЕГО класса
}
}
// Использование:
let a = RecipeIngredient() // Использует convenience Food.init() -> RecipeIngredient.init(name:quantity:)
let b = RecipeIngredient(name: "Salt") // Использует convenience RecipeIngredient.init(name:)
let c = RecipeIngredient(name: "Eggs", quantity: 6) // Использует designated
Почему это важно? Эти правила обеспечивают безопасность и предсказуемость процесса инициализации, гарантируя, что все свойства будут проинициализированы до первого использования объекта, даже в сложных иерархиях наследования.