После входа экземпляра класса в deinit когда ARC освобождает его сильные stored свойства?

После входа экземпляра класса в deinit когда ARC освобождает его сильные stored-свойства?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Сильные stored-свойства экземпляра остаются доступными и удерживают свои объекты во время выполнения тела deinit. После завершения тела deinit ARC разрушает хранилище экземпляра и освобождает эти сильные ссылки; если какая-либо из них была последней, связанный объект может быть уничтожен в этот момент.

Исторический контекст

ARC автоматизировал операции retain и release, которые при ручном управлении памятью требовалось расставлять самостоятельно. При этом Swift сохранил детерминированную модель подсчёта ссылок: уничтожение объекта начинается, когда исчезает его последняя сильная ссылка.

Разделение вызова deinit и последующего разрушения хранилища позволяет коду deinit корректно выполнить финализацию, пока состояние экземпляра ещё доступно.

Постановка проблемы

Неверное предположение, что stored-свойства уже освобождены к моменту входа в deinit, приводит к обращениям к недоступному состоянию или к преждевременной ручной очистке ресурсов. Обратная ошибка — считать, что strong-свойства освобождаются сразу после начала deinit, — может привести к неправильным ожиданиям относительно порядка deinit связанных объектов.

Важно отличать сильные stored-свойства от weak и unowned ссылок: слабая ссылка не удерживает объект, а невладеющая ссылка не увеличивает его счётчик сильных ссылок.

Подробное решение

При обнаружении последней сильной ссылки ARC запускает разрушение экземпляра. Сначала выполняется тело его deinit, поэтому сильные stored-свойства ещё существуют, а объекты, на которые они ссылаются, всё ещё удерживаются этим экземпляром.

После завершения deinit ARC разрушает stored-свойства и освобождает их значения. Для свойства классового типа это означает уменьшение счётчика сильных ссылок на связанный объект. Если счётчик становится равен нулю, его deinit может быть вызван непосредственно в ходе такого разрушения.

final class Child { deinit { print("child") } } final class Owner { let child = Child() deinit { print(child is Child) print("owner") } } var owner: Owner? = Owner() owner = nil

В теле deinit Owner свойство child ещё доступно. После завершения deinit Owner его сильная ссылка освобождается, поэтому затем становится возможным вызов Child.deinit, если других сильных владельцев нет. Точный момент машинных retain/release-операций может оптимизироваться компилятором, но семантически нельзя использовать объект после завершения его deinit.

Не следует вручную обнулять сильные свойства только ради их освобождения: это меняет состояние объекта и может вызвать deinit вложенного объекта ещё внутри тела deinit. Обычно достаточно позволить ARC разрушить хранилище естественным образом.

Ситуация из практики

В менеджере ресурсов stored-свойство connection содержит объект соединения, а deinit менеджера записывает в журнал идентификатор соединения. Если обнулить connection в начале deinit, соединение может закрыться до записи в журнал, а его deinit выполнится в неожиданной точке.

Вариант с ручным обнулением даёт раннее освобождение ресурса, но усложняет порядок уничтожения и может вызвать побочные эффекты прямо внутри deinit. Вариант с обычным завершением deinit сохраняет свойство доступным для финализации и оставляет освобождение под контролем ARC.

Практическое решение — не изменять сильные stored-свойства без необходимости, а явно освобождать ресурс только тогда, когда раннее освобождение действительно является частью контракта класса. Это делает порядок завершения предсказуемее и уменьшает риск обращения к уже освобождённому связанному состоянию.

Что кандидаты часто упускают

1. Может ли stored-свойство быть доступно в deinit, если оно содержит optional?

Да. Если это сильное optional-свойство, его текущее значение доступно в deinit. При значении nil освобождать нечего; при наличии экземпляра он продолжает удерживаться до разрушения самого свойства или до явного присваивания другого значения.

2. Освобождаются ли weak-свойства после deinit так же, как strong-свойства?

Нет. Weak-ссылка не владеет объектом и не удерживает его до разрушения контейнера. При уничтожении объекта runtime обнуляет связанные weak-ссылки, поэтому в deinit контейнера weak-свойство может уже быть nil, даже если соответствующее хранилище самого контейнера ещё существует.

3. Можно ли полагаться на порядок deinit объектов, хранимых в нескольких strong-свойствах?

Нельзя строить прикладную логику на порядке уничтожения независимых свойств. ARC освобождает хранилище в рамках правил разрушения объекта, но код должен быть корректен независимо от порядка, если свойства не образуют явно заданную зависимость. Нужный порядок следует выражать явным методом завершения или протоколом владения, а не косвенно ожидать его от deinit.