В коде сохраняют ObjectIdentifier экземпляра, чтобы использовать его позже как идентификатор. Оцените, може...

В коде сохраняют ObjectIdentifier экземпляра, чтобы использовать его позже как идентификатор. Оцените, может ли такая переменная удержать объект живым:

final class Session {
    deinit { print("deinit") }
}

var session: Session? = Session()
let identifier = ObjectIdentifier(session!)
session = nil
print(identifier)
Проходите собеседования с ИИ помощником Hintsage

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

Нет, ObjectIdentifier не удерживает экземпляр и не увеличивает его strong reference count. После присваивания session = nil объект может быть уничтожен сразу, если других сильных ссылок нет; переменная identifier при этом продолжит содержать лишь значение идентификатора.

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

ARC автоматизировал управление сильными ссылками, но не может управлять временем жизни по произвольным числовым значениям или адресам. Для задач идентификации объект можно представить значением ObjectIdentifier, не превращая сам идентификатор во владельца объекта.

Такой подход отделяет две задачи: владение экземпляром и сравнение его идентичности. Это удобно для таблиц, множеств и отладки, но идентификатор не является резервированием памяти за объектом.

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

Если сохранить только ObjectIdentifier, можно ошибочно считать объект живым, потому что идентификатор продолжает существовать. На самом деле ARC учитывает только реальные сильные ссылки, а не значения, косвенно связанные с адресом экземпляра.

После уничтожения объекта его адрес в памяти может быть повторно использован другим объектом. Поэтому старый ObjectIdentifier нельзя считать бессрочным или уникальным идентификатором сущности.

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

ObjectIdentifier — это значение, позволяющее сравнивать идентичность экземпляров ссылочного типа. Пока два экземпляра живы, разные объекты имеют разные идентификаторы, а копирование самого идентификатора не меняет граф владения.

В примере identifier не содержит сильной ссылки на Session. После обнуления session ARC уменьшает число сильных владельцев; при достижении нуля вызывается deinit, если объект больше ничем не удерживается.

После уничтожения экземпляра значение identifier не становится автоматически недействительным в типовой системе Swift. Оно просто больше не даёт безопасного доступа к прежнему объекту. Если память будет переиспользована, новый объект может получить тот же идентификатор.

Поэтому ObjectIdentifier подходит для краткоживущего сравнения идентичности или ключа во время гарантированной жизни объектов. Для устойчивого идентификатора сущности нужен отдельный идентификатор, например сохранённый UUID, а не адрес объекта.

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

Менеджер отслеживает активные контроллеры в словаре по ObjectIdentifier и удаляет запись при закрытии контроллера. Если словарь хранит только идентификаторы, он не продлевает жизнь контроллеров и может содержать устаревшую запись после их уничтожения.

Вариант со словарём [ObjectIdentifier: Controller] удерживает контроллеры сильными значениями. Это предотвращает исчезновение объектов, но может создать утечку, если записи не удаляются своевременно.

Вариант со словарём [ObjectIdentifier: WeakBox<Controller>] не удерживает контроллеры и не создаёт такую утечку, но требует очистки пустых элементов. Выбор зависит от цели: для реестра живых экземпляров нужен контроль очистки, а не доверие к самому ObjectIdentifier.

final class Controller { let id = UUID() } var controller: Controller? = Controller() let objectID = ObjectIdentifier(controller!) let stableID = controller!.id controller = nil // objectID не удерживает Controller. // stableID остаётся идентификатором сущности.

objectID связан с конкретным временем жизни экземпляра, а stableID — самостоятельное значение, которое можно передавать и хранить после уничтожения объекта.

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

  1. Можно ли по сохранённому ObjectIdentifier восстановить экземпляр?

    Нет. ObjectIdentifier не является обратной ссылкой и не предоставляет операции поиска объекта. Для восстановления нужна отдельная структура данных, которая сама должна хранить объект или слабую обёртку.

  2. Всегда ли разные экземпляры получают разные ObjectIdentifier?

    Только одновременно живущие экземпляры должны иметь различимые идентификаторы. После уничтожения одного объекта его адрес может быть переиспользован, поэтому идентификатор старого объекта способен совпасть с идентификатором нового.

  3. Безопасно ли использовать ObjectIdentifier как ключ кэша?

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