Считается ли weak ссылка на экземпляр второй сильной ссылкой при проверке isKnownUniquelyReferenced?

Считается ли weak-ссылка на экземпляр второй сильной ссылкой при проверке isKnownUniquelyReferenced?

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

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

Нет. weak-ссылка не считается сильным владельцем и не делает проверку isKnownUniquelyReferenced ложной. Если у экземпляра остаётся ровно одна сильная ссылка, проверка может вернуть true, даже когда существуют слабые ссылки на тот же объект.

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

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

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

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

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

Если ошибочно считать каждую weak-ссылку вторым владельцем, copy-on-write будет без необходимости создавать копии. Если, наоборот, принять уникальность за гарантию отсутствия конкурентного доступа, можно получить гонку данных: проверка ARC не является механизмом синхронизации.

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

isKnownUniquelyReferenced проверяет наличие единственного сильного владения экземпляром. weak-ссылки не увеличивают счётчик сильных ссылок, не продлевают время жизни объекта и поэтому не нарушают такую уникальность.

final class Storage { var value = 0 } var storage = Storage() weak var observer = storage print(isKnownUniquelyReferenced(&storage)) // true let anotherOwner = storage print(isKnownUniquelyReferenced(&storage)) // false

В первом вызове storage — единственная сильная ссылка, а observer лишь наблюдает за объектом. После создания anotherOwner появляются два сильных владельца, поэтому изменение объекта «на месте» уже может затронуть другую логическую копию.

Название isKnown существенно: результат может быть консервативным, то есть false не обязательно доказывает наличие другого владельца. true следует использовать только как разрешение на оптимизацию, а не как общий признак отсутствия ссылок или доступа из других потоков.

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

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

В приложении есть copy-on-write буфер изображений. Отладочный монитор хранит weak-ссылку на внутреннее хранилище, чтобы показывать статистику, но не должен владеть буфером.

Вариант с сильной ссылкой от монитора прост, однако он продлевает жизнь буфера и делает проверку уникальности ложной. Вариант с unowned не создаёт владения, но опасен: монитор может обратиться к уже уничтоженному буферу. Отсутствие ссылки полностью устраняет риск, но лишает монитор возможности наблюдать за объектом.

Выбранный вариант — weak: он сохраняет корректную семантику необязательного наблюдения и не мешает copy-on-write использовать уникальность сильного владельца. Монитор должен обрабатывать nil, а изменения самого буфера всё равно должны быть защищены от конкурентного доступа отдельно.

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

  1. Означает ли true от isKnownUniquelyReferenced, что объект никто больше не читает?

    Нет. Результат говорит только об известной уникальности сильного владения. Слабые ссылки, невладеющие ссылки и внешние небезопасные способы доступа могут существовать; кроме того, другой поток может одновременно читать или изменять состояние. Проверка предназначена для оптимизации владения, а не для синхронизации.

  2. Продлевает ли вызов isKnownUniquelyReferenced жизнь объекта?

    Устойчивая сильная ссылка, переданная для проверки, уже должна существовать. Сам вызов не превращает weak-ссылку в сильную и не создаёт долгоживущего владельца. Если объект доступен только через weak, чтение слабой ссылки сначала должно получить временную сильную ссылку; если объект уже уничтожен, результатом будет nil, а не успешная проверка уникальности.

  3. Почему наличие слабой ссылки не требует копирования ссылочного буфера?

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