Представьте, что инициализатор класса завершился с ошибкой после создания вложенного объекта: кто освободит уже созданные объекты?
При неудачном завершении инициализатора ARC автоматически освобождает уже инициализированные сильные свойства создаваемого экземпляра. deinit самого класса при этом не вызывается, потому что экземпляр не завершил инициализацию.
До ARC разработчик вручную управлял подсчётом ссылок и должен был освобождать все уже созданные ресурсы на каждой ветке ошибки. Частичный сбой инициализации был особенно опасен: пропущенное освобождение приводило к утечке, а преждевременное — к обращению к освобождённому объекту.
ARC автоматизирует эти операции, включая очистку частично инициализированного экземпляра. Это позволяет описывать только условия ошибки, не дублируя ручную логику освобождения зависимостей.
Инициализатор может создать несколько объектов, а затем обнаружить некорректные входные данные или недоступную зависимость. Если один из уже созданных объектов хранится в сильном свойстве, при возврате nil или выбросе ошибки это свойство не должно остаться удерживать объект.
Важно отличать сам экземпляр, инициализация которого не завершилась, от объектов, которыми он владеет. Сам экземпляр не проходит обычный жизненный цикл и не вызывает deinit, но его уже инициализированные свойства должны быть корректно очищены.
Когда инициализатор завершается неуспешно, ARC освобождает сильные stored-свойства, которые успели получить значения. Для каждого такого свойства уменьшается счётчик сильных ссылок; если это была последняя сильная ссылка, соответствующий объект деинициализируется.
deinit внешнего класса не вызывается: он предназначен для полностью созданного экземпляра. Однако deinit вложенных объектов может быть вызван во время очистки, если после освобождения свойств у них не осталось других владельцев.
В этом примере при неуспешной инициализации resource освобождается, поэтому может появиться сообщение Resource released. Сообщение Session released не появится: объект Session не был полностью инициализирован.
Если созданный объект одновременно удерживается другой сильной ссылкой, очистка свойства лишь уменьшит счётчик ссылок, но не уничтожит объект. Локальные сильные переменные и параметры также освобождаются по правилам ARC; конкретный момент release может оптимизироваться и не обязан совпадать с концом текстовой области видимости.
Для ресурсов, не представленных объектами Swift, например файловых дескрипторов или временных системных идентификаторов, ARC сам по себе не знает, как выполнять специальное закрытие. Такой ресурс нужно освобождать в подходящем обработчике ошибки или оборачивать в объект, чей deinit выполняет необходимую очистку.
Класс Session создаёт сетевой транспорт, сохраняет его в свойстве, а затем проверяет токен авторизации. При неверном токене инициализатор возвращает nil.
Вариант с ручным обнулением свойства перед каждым return сложен в сопровождении: при добавлении нового свойства легко забыть одну из веток очистки. Вариант с временным объектом уменьшает число частично инициализированных свойств, но не всегда подходит, если объект должен стать частью состояния экземпляра после успешной инициализации.
Оптимальное решение — хранить объект в сильном свойстве и завершать инициализатор через nil или ошибку. ARC очистит это свойство автоматически; после исправления токена экземпляр будет создан обычным образом, а при ошибке не останется утечки. Для внешних ресурсов всё равно нужен явный протокол освобождения либо специальный владеющий объект.
deinit класса, если его инициализатор вернул nil?Нет. deinit вызывается только для экземпляра, который был полностью инициализирован и затем стал недоступен из-за отсутствия сильных ссылок. При сбое инициализации Swift освобождает уже инициализированные свойства без вызова deinit самого незавершённого экземпляра.
Он станет доступен для уничтожения после освобождения сильного свойства, но уничтожение произойдёт только при отсутствии других сильных ссылок. Если тот же объект передан вызывающим кодом и продолжает удерживаться там, его deinit не вызовется во время очистки неудачного инициализатора.
ARC управляет только ссылками на объекты, а не произвольными ресурсами ОС. Локальный объект Swift будет освобождён по правилам ARC, но файл, дескриптор или блокировку нужно закрыть явно или передать объекту-обёртке с корректным deinit. Поэтому автоматическая очистка памяти не заменяет управление внешними ресурсами.