В классе convenience-инициализатор пытается напрямую вызвать инициализатор суперкласса. Какое правило Swift нарушено?
convenience-инициализатор обязан делегировать создание объекта другому инициализатору того же класса, а не напрямую вызывать инициализатор суперкласса. Поэтому вызов super.init из такого инициализатора запрещён.
Прямой вызов инициализатора суперкласса выполняется из designated-инициализатора подкласса. Convenience-инициализатор может только выбрать или подготовить удобный способ вызова designated-инициализатора своего класса.
Модель инициализации Swift разделяет инициализаторы на два уровня: designated отвечает за полное создание экземпляра, а convenience предоставляет альтернативный способ его сконструировать.
Такое разделение устраняет несколько независимых путей частичной инициализации и помогает гарантировать, что все хранимые свойства будут корректно установлены до завершения создания объекта.
Если convenience-инициализатор напрямую обращается к super.init, он обходит обязательное делегирование внутри собственного класса. В результате логика инициализации, расположенная в designated-инициализаторе этого класса, может не выполниться.
Это опасно не только из-за нарушения синтаксического правила. Можно пропустить установку свойств, инварианты класса или подготовку состояния, на которую рассчитывают другие методы и инициализаторы.
Designated-инициализатор подкласса должен сначала инициализировать все свойства, добавленные подклассом, затем вызвать designated-инициализатор суперкласса. После этого завершается вторая фаза инициализации, включая настройку унаследованного состояния.
Convenience-инициализатор не является самостоятельным маршрутом создания объекта. Он обязан вызвать другой инициализатор того же класса через self.init(...). Цепочка convenience-инициализаторов в итоге должна прийти к designated-инициализатору.
После делегирования convenience-инициализатор уже может выполнять дополнительную настройку доступных свойств. Но до делегирования он не может использовать self, обращаться к свойствам или вызывать методы экземпляра, поскольку объект ещё не прошёл необходимую инициализацию.
Здесь init() не создаёт объект самостоятельно: он делегирует работу designated-инициализатору init(name:). Если бы init() пытался вызвать super.init(...), компилятор отклонил бы код, потому что convenience-инициализатор может делегировать только внутри текущего класса.
В подклассе designated-инициализатор может вызвать super.init, а convenience-инициализатор сначала делегирует designated-инициализатору этого же подкласса. Это сохраняет единый контролируемый путь инициализации.
В базовом классе Account designated-инициализатор проверяет имя и устанавливает обязательное состояние. В подклассе PremiumAccount нужен короткий инициализатор без параметров.
Можно продублировать в convenience-инициализаторе код установки свойств и попытаться вызвать суперкласс. Минус такого подхода — нарушение правил Swift и риск расхождения логики при последующих изменениях.
Можно сделать короткий инициализатор designated и в нём вызвать super.init. Это корректно, но если инициализатор лишь подставляет значения по умолчанию, отдельный designated-путь может быть избыточным.
Оптимальный вариант — convenience-инициализатор PremiumAccount, который передаёт значения в designated-инициализатор PremiumAccount, а тот вызывает super.init. В итоге базовые проверки выполняются один раз, а все экземпляры проходят одинаковый корректный маршрут создания.
Да, но только после делегирования через self.init(...). До этого экземпляр ещё не считается инициализированным, поэтому доступ к его свойствам и методам запрещён. Кроме того, если свойство доступно только для чтения после инициализации, изменить его позднее нельзя.
Да. Цепочка делегирования может проходить через другие convenience-инициализаторы, но должна завершиться designated-инициализатором текущего класса. Иначе обязательная инициализация состояния не имеет гарантированной точки завершения.
super.init даже после установки собственных свойств?Потому что его роль — предоставить альтернативный вход в инициализацию текущего класса, а не заменить designated-инициализатор. Прямой вызов super.init обходит designated-инициализаторы подкласса и может пропустить их инварианты. Если требуется такая последовательность, её следует реализовать в designated-инициализаторе подкласса, а convenience-инициализатор должен делегировать ему.