Инициализатор
Бланк Temperature хранит температуру в градусах Цельсия. Но ваш термометр показывает градусы Фаренгейта, и пересчитывать их в уме перед каждым заполнением неудобно. Удобнее отдать бланк сотруднику: вы называете градусы Фаренгейта, а он сам пересчитывает их и вписывает в графу.
Такого сотрудника в структуре называют инициализатором:
struct Temperature {
var celsius: Double
init(fahrenheit: Double) {
celsius = (fahrenheit - 32) * 5 / 9
}
}
let boiling = Temperature(fahrenheit: 212)
print(boiling.celsius) // 100.0Давайте разберём программу по шагам:
- Внутри
Temperatureобъявленinit(fahrenheit:). Перед скобками с параметрами нет ниfunc, ни имени, а после них нет->. Temperature(fahrenheit: 212)вызывает этотinit: параметрfahrenheitполучает212.- Тело пересчитывает градусы и кладёт результат в свойство
celsius. print(boiling.celsius)печатает100.0: вода кипит при 212 градусах Фаренгейта.
Свой инициализатор
Инициализатор — это код, который заполняет свойства нового экземпляра. Его объявляют словом init, а вызывают по имени типа: Temperature(fahrenheit: 212). Параметры, метки аргументов и значения по умолчанию у него такие же, как у функции. mutating инициализатору не нужен: заполнять свойства нового экземпляра — его работа.
Правило у инициализатора одно: к его концу у каждого хранимого свойства должно быть значение, своё или по умолчанию. Если какое-то свойство останется пустым, компилятор ответит return from initializer without initializing all stored properties.
Здесь картинка с сотрудником неточна. Сотрудник может по рассеянности отдать бланк с пустой графой, а Swift такой init даже не скомпилирует.
Наблюдатели willSet и didSet внутри init не срабатывают: экземпляр ещё только создаётся.
Почленный инициализатор пропадает
Способ создать экземпляр с метками всех свойств, вроде Point(x: 1, y: 2), Swift даёт структуре сам. Его называют почленным инициализатором. Но как только в структуре появляется свой init, почленный пропадает:
let room = Temperature(celsius: 20) // ✗error: incorrect argument label in call (have 'celsius:', expected 'fahrenheit:')Компилятор видит единственный инициализатор, init(fahrenheit:), и подсказывает, что метка должна быть fahrenheit:. Если нужны оба способа, почленный дописывают сами, ещё одним init внутри структуры: init(celsius: Double) { self.celsius = celsius }. Теперь у Temperature два инициализатора, и Swift выбирает нужный по меткам.
Частая ошибка
Параметр инициализатора назвали так же, как свойство, и забыли self:
struct Box {
var width: Int
init(width: Int) {
width = width // ✗
}
}error: cannot assign to value: 'width' is a 'let' constantПрограмма не компилируется. Внутри init имя width означает параметр, а параметр — константа. Запись width = width пытается положить параметр в него же. Чтобы обратиться к свойству, напишите self.width = width: слева свойство экземпляра, справа параметр.
Главное
initзаполняет свойства нового экземпляра; к его концу у каждого хранимого свойства должно быть значение.- Свой
initубирает почленный инициализатор; если почленный нужен, его дописывают ещё однимinit. - Когда параметр и свойство называются одинаково, свойство пишут через
self:self.width = width.