При присваивании экземпляра классового типа переменной any Protocol становится ли эта переменная его сильным владельцем?
Да. Обычная переменная типа any Protocol, содержащая экземпляр класса, хранит сильную ссылку на этот экземпляр. Пока существует хотя бы одна такая сильная ссылка, ARC не может уничтожить объект.
Протоколы позволяют отделить код от конкретного класса и работать с объектом через абстрактный интерфейс. Для этого Swift поддерживает existential-тип any Protocol, который может хранить значение конкретного реализующего типа.
При этом абстрагирование типа не должно неожиданно менять управление временем жизни объекта. Поэтому для экземпляра класса existential-переменная сохраняет обычную семантику сильного владения, если явно не используется другой способ хранения ссылки.
Тип any Protocol скрывает конкретный класс, из-за чего может возникнуть ошибочное предположение, что объект внутри existential-контейнера является лишь временным значением. Если присваивание такой переменной не учитывать как создание сильной ссылки, можно неверно определить момент вызова deinit.
При переназначении или уничтожении existential-переменной её прежняя сильная ссылка освобождается. Объект станет доступен для уничтожения только тогда, когда после этого не останется других сильных ссылок.
Для экземпляра классового типа присваивание в any Protocol обычно означает сохранение ссылки на тот же объект, а не создание его копии. ARC увеличивает количество сильных владельцев при сохранении ссылки и уменьшает его, когда existential-переменная очищается, переназначается или выходит из времени жизни.
В примере existential-переменная владеет экземпляром Worker. Присваивание nil освобождает её сильную ссылку; после этого ARC вызывает deinit, если объект больше нигде не удерживается.
Копирование самой existential-переменной также не копирует экземпляр класса. Создаётся ещё одна сильная ссылка на тот же объект, поэтому его время жизни продлевается, но состояние и identity остаются общими.
Внутреннее представление existential-значения может содержать данные о типе и само значение либо ссылку на него. Конкретная оптимизация размещения и retain/release-операций не является контрактом, но она не меняет наблюдаемую семантику владения: сильная existential-ссылка должна сохранять объект живым до окончания её гарантированного времени жизни.
Сервис передаётся компоненту приложения через протокол, чтобы компонент не зависел от конкретной реализации. Разработчик считает, что после выхода локальной переменной сервис будет уничтожен, хотя компонент сохранил его в свойстве типа any Service.
Вариант с сильным свойством прост и безопасен: компонент гарантированно получает рабочий сервис, но может непреднамеренно продлить его жизнь. Вариант с ручным управлением или небезопасными ссылками уменьшает удержание, однако создаёт риск обращения к уже уничтоженному объекту.
Выбранное решение — оставить сильное хранение, если компонент действительно должен владеть сервисом, и явно определить жизненный цикл компонента. В результате сервис живёт ровно столько, сколько существует владеющий им компонент, а освобождение происходит предсказуемо при удалении последней сильной ссылки.
Вопрос 1. Увеличивает ли присваивание экземпляра в any Protocol число экземпляров?
Нет. Экземпляр класса не копируется: existential хранит ссылку на тот же объект. При создании дополнительной сильной existential-ссылки увеличивается число владельцев, но identity объекта, его адресуемое состояние и deinit остаются общими.
Вопрос 2. Может ли объект уничтожиться, пока существует обычная переменная any Protocol, содержащая его?
При обычном сильном хранении — нет. Существующая переменная удерживает объект живым, даже если конкретный динамический тип скрыт. Исключения связаны не с any Protocol как таковым, а с использованием слабых, невладеющих или небезопасных ссылок либо с завершением времени жизни самой переменной по правилам Swift.
Вопрос 3. Гарантирует ли existential-контейнер выделение отдельного объекта в куче?
Нет, такой гарантии нет. Представление existential-значения и оптимизации размещения выбираются реализацией Swift; контейнер может хранить данные различными способами. Для экземпляра класса это не отменяет главного правила: содержащая его обычная переменная владеет ссылкой на объект, а не отдельной копией объекта.