Что происходит с числом сильных ссылок на объект при присваивании его weak-ссылке?
Присваивание объекта weak-ссылке не увеличивает число его сильных ссылок. Такая ссылка лишь регистрируется в механизме ARC и автоматически становится nil, когда объект уничтожен.
Поэтому weak-ссылка не продлевает время жизни объекта. Если после присваивания других сильных ссылок не осталось, объект может быть немедленно освобождён, а weak-ссылка обнулится.
ARC появился, чтобы автоматизировать управление подсчётом ссылок, которое раньше требовало ручных операций сохранения и освобождения объектов. При этом для отношений без владения понадобился отдельный механизм: ссылка должна указывать на объект, но не удерживать его в памяти.
weak решает именно эту задачу и дополнительно устраняет висячие ссылки: после уничтожения объекта все связанные слабые ссылки переводятся в nil.
Представим объект, на который временно ссылается наблюдатель, делегат или реестр подписчиков. Если такая ссылка будет сильной, наблюдатель может случайно удерживать объект дольше необходимого и создать утечку памяти.
Если использовать обычный указатель или unowned без гарантии времени жизни объекта, обращение после уничтожения приведёт к ошибке выполнения. weak избегает этого риска, но требует корректно обрабатывать отсутствие объекта через optional-значение.
При присваивании объекта weak-ссылке ARC не добавляет сильное владение. Сильный счётчик объекта остаётся прежним, а среда выполнения сохраняет информацию о слабой ссылке, чтобы впоследствии безопасно обнулить её.
Пока token содержит сильную ссылку, объект жив. После присваивания nil последняя сильная ссылка исчезает, ARC уничтожает объект, а observer автоматически становится nil.
Важное следствие: количество weak-ссылок не влияет на момент уничтожения объекта. Они требуют служебного учёта, но не являются владельцами. При чтении слабой ссылки нужно учитывать, что результат может отсутствовать, поэтому тип weak-ссылки является optional.
weak подходит, когда объект может исчезнуть раньше ссылки и это считается нормальным сценарием. Цена безопасности — проверка на nil и отсутствие гарантии, что объект останется доступным между разными операциями.
Центр событий хранит подписчиков, но не должен владеть объектами экранов. Если хранить подписчика сильной ссылкой, закрытый экран может остаться в памяти из-за регистрации в центре событий. Это приводит к утечке и удержанию связанного графа объектов.
Вариант с unowned не требует optional-проверок, но опасен: центр событий может отправить событие после уничтожения экрана, что вызовет ошибку выполнения. Вариант с weak не удерживает экран и автоматически становится nil после его уничтожения.
Практическое решение — хранить подписчика слабо, удаляя nil-элементы при очередной рассылке или очистке коллекции. В результате экран освобождается после исчезновения его собственных владельцев, а центр событий не обращается к уничтоженному объекту.
Удаляет ли ARC weak-ссылку сразу после исчезновения последней сильной ссылки?
Нет, сначала объект становится кандидатом на уничтожение и выполняется его освобождение, включая deinit. Затем связанные слабые ссылки обнуляются, чтобы ни одна из них не указывала на уже уничтоженный объект. Для программы результатом является nil, но порядок внутренних действий не следует смешивать с простым удалением значения из переменной.
Может ли объект остаться в памяти, если на него существуют только weak-ссылки?
Нет, weak-ссылки не поддерживают его время жизни. Как только исчезает последняя сильная ссылка, ARC получает право уничтожить объект, даже если слабых ссылок осталось много. Эти ссылки нужны только для безопасного обнаружения того, что объект больше недоступен.
Почему weak-ссылку нельзя использовать как замену сильной ссылке внутри длительной операции?
Потому что наличие weak-ссылки не гарантирует сохранность объекта. Объект может быть уничтожен между двумя обращениями к слабой ссылке, если другой поток или часть программы удалит последнюю сильную ссылку.
Если объект нужно использовать непрерывно в рамках операции, его следует получить в сильную локальную переменную, например через optional-binding. Такая локальная ссылка временно удержит объект до завершения нужного участка кода; сама исходная weak-ссылка при этом по-прежнему не становится владеющей.