В Swift все свойства нес optional-типов должны быть инициализированы до конца инициализации экземпляра. Для этого используются инициализаторы: designated (основные), convenience (вспомогательные) и required (обязательные для наследников).
Пример:
class Vehicle { let numberOfWheels: Int required init(numberOfWheels: Int) { self.numberOfWheels = numberOfWheels } } class Car: Vehicle { let brand: String // Designated initializer required init(numberOfWheels: Int) { self.brand = "Unknown" super.init(numberOfWheels: numberOfWheels) } // Convenience convenience init() { self.init(numberOfWheels: 4) self.brand = "Ford" } }
Ключевые особенности:
В каком порядке необходимо инициализировать свойства подкласса и суперкласса при использовании designated initializer?
Ответ: Все свойства подкласса должны быть проинициализированы до вызова designated инициализатора суперкласса (super.init), иначе будет ошибка компиляции. После super.init нельзя инициализировать новые stored-свойства подкласса, только использовать их.
class Parent { let parentProp: Int init(prop: Int) { parentProp = prop } } class Child: Parent { let childProp: String init(prop: Int, childProp: String) { self.childProp = childProp // Сначала свои свойства super.init(prop: prop) // Потом parent } }
История
Разработчик в convenience инициализаторе попытался напрямую присвоить значение require-свойству, не вызвав designated. В результате свойство было инициализировано дважды с разными значениями, что вело к неожиданному поведению при тестировании.
История
При наследовании deep class hierarchy забыли объявить required init, что привело к невозможности корректной инициализации производного типа из фреймворка через reflection, и крашу при десериализации JSON-модели.
История
В приложении во время расширения класса Vehicle не был вызван designated инициализатор суперкласса, что привело к некорректной инициализации обязательных свойств и runtime crash в production после обновления схемы данных.