Сценарий: массив — единственный владелец экземпляра класса и содержит его дважды. Может ли удаление одного элемента сразу вызвать deinit?
Нет. Каждый элемент массива хранит отдельную сильную ссылку на экземпляр, поэтому после удаления одного из двух элементов остаётся ещё одна сильная ссылка. deinit сможет выполниться только после удаления второго элемента и освобождения всех других сильных ссылок.
ARC автоматизирует управление временем жизни объектов на основе подсчёта сильных ссылок. Такой подход избавляет разработчика от ручных retain и release, но требует понимать, что учитываются не переменные как таковые, а все сильные владения объектом, включая элементы коллекций.
Один и тот же объект можно поместить в массив несколько раз. Внешне это один экземпляр, но каждое хранение в массиве создаёт отдельную сильную ссылку.
Ошибка возникает, если считать, что удаление первого совпадения удаляет объект целиком. Это может привести к неправильному ожиданию момента вызова deinit и ошибочным выводам при диагностике утечек или освобождения памяти.
Для обычного массива элементов ссылочного типа каждое добавление экземпляра создаёт сильное владение. Поэтому при двух вхождениях одного объекта его условный счётчик сильных ссылок увеличивается дважды относительно состояния без массива.
После первого removeFirst() второе вхождение продолжает удерживать объект. После второго удаления массив больше не владеет им, но в примере остаётся сильная ссылка session, поэтому deinit фактически не обязан вызываться до выхода session из области жизни.
Точный момент уничтожения зависит от всех владельцев: локальных переменных, свойств, других коллекций, временных значений и копий массива. При копировании массива элементы обычно используют copy-on-write, однако пока копии разделяют хранилище, объект остаётся удерживаемым всеми логическими вхождениями, доступными через эти копии.
Главный вывод: число экземпляров объекта и число сильных ссылок на него — разные величины. Один экземпляр может иметь множество сильных владельцев, в том числе несколько элементов одной коллекции.
Кэш хранил объекты в массиве, а при повторном добавлении не проверял, что экземпляр уже присутствует. Разработчик удалял одну запись и ожидал вызова deinit, но объект продолжал жить.
Вариант с удалением только первого совпадения прост, но не устраняет повторные сильные ссылки. Полный поиск и удаление всех совпадений решает проблему конкретного массива, однако может быть дорогим и не предотвращает появление дублей.
Оптимальное решение — хранить экземпляр один раз, например по уникальному идентификатору в словаре, если повторное владение не имеет бизнес-смысла. Если же дублирование является частью модели, время жизни нужно связывать с удалением всех записей и учитывать остальные сильные ссылки. В результате диагностика должна анализировать граф владения, а не только количество уникальных объектов.
Удаляет ли присваивание массива пустого значения все сильные ссылки на его элементы?
Нет, только ссылки, находившиеся в этом конкретном массиве. Объекты продолжат жить, если их удерживают локальные переменные, свойства, другие копии массива или любые другие сильные ссылки.
Становится ли объект слабым из-за повторного хранения в массиве?
Нет. Элементы обычного массива имеют сильное владение, если элементом не является специальная обёртка для слабой ссылки. Поэтому массив сам по себе обычно продлевает жизнь помещённых в него экземпляров.
Можно ли по вызову deinit определить, какой элемент массива был удалён последним?
Нет. deinit относится к экземпляру, а не к конкретной ссылке или позиции в массиве. Он вызывается только после исчезновения последнего сильного владения, поэтому без дополнительного учёта владельцев нельзя установить, какая операция стала решающей.