Ответ
Инициализаторы в 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
Почему это важно? Эти правила обеспечивают безопасность и предсказуемость процесса инициализации, гарантируя, что все свойства будут проинициализированы до первого использования объекта, даже в сложных иерархиях наследования.
Ответ 18+ 🔞
А, ну вот, опять эти инициализаторы в Swift, блядь! Сидишь такой, думаешь — ну что за ёперный театр, нахуй столько правил? А потом бац — и оказывается, там всё логично, просто мозги надо включить, а не в рот себе чих-пых!
Смотри, есть два типа этих ваших инициализаторов, и они как отец и сын — один главный, а другой ленивый по жизни.
Designated (Назначенные) — это как главный пахан. Его работа — всё проинициализировать до конца, чтоб ни одно свойство не осталось без значения, как сирота казанская. А если это класс-наследник, то он ещё и суперклассу должен позвонить, типа «батя, я тут всё сделал, твою часть тоже проставь». И может он звонить только своему прямому предку, а не через голову какому-нибудь прадеду. И такой пахан в классе должен быть хотя бы один, а то кто порядок наведёт?
Convenience (Вспомогательные) — это как хитрая жопа. Его задача — сделать тебе красиво и удобно. Не хочешь все параметры вписывать? На, вот тебе инициализатор с дефолтными значениями. Он сам-то нихуя не делает по сути, только в итоге обязан позвать designated своего же класса, а не чужого. То есть он как менеджер-посредник: берёт твои упрощённые данные, подготавливает, а потом — «Герасим, на, разбирайся!».
Правила, блядь, простые, но железные:
- Designated зовёт designated своего родителя (если он есть).
- Convenience зовёт другой инициализатор своего же класса.
- И вся эта цепочка 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. Бате отзвонился
}
// CONVENIENCE, который переопределяет родительский
override convenience init(name: String) {
self.init(name: name, quantity: 1) // И тоже зовёт designated СВОЕГО класса!
}
}
// А теперь используем эту красоту:
let a = RecipeIngredient() // По цепочке: Food.convenience() -> RecipeIngredient.init(name:quantity:)
let b = RecipeIngredient(name: "Salt") // RecipeIngredient.convenience(name:) -> тот же designated
let c = RecipeIngredient(name: "Eggs", quantity: 6) // Напрямую в designated
И вся эта, блядь, карусель нужна для одного — чтобы твой объект родился здоровым и полноценным, со всеми проинициализированными свойствами. Чтобы не вышло так, что ты его уже используешь, а у него внутри какое-то свойство nil болтается, как манда с ушами. Swift этого не любит, он за безопасность, сука. Так что учи правила, не распиздуйся!