Представьте объект, на который слабо ссылаются тысячи владельцев. Как ARC обнулит эти ссылки при уничтожении объекта без полного перебора памяти?
ARC использует специальное внутреннее учётное хранилище для weak-ссылок объекта, поэтому при его уничтожении runtime обращается к зарегистрированным слабым ссылкам и обнуляет их. Полный перебор всей памяти процесса не требуется.
ARC появился как автоматизация ручного управления подсчёком ссылок, чтобы разработчик не расставлял операции удержания и освобождения объекта вручную. При этом слабые ссылки потребовали отдельного механизма: они не должны увеличивать счётчик сильных ссылок, но должны становиться безопасными после уничтожения объекта.
Слабая ссылка не владеет объектом. Если объект уничтожен, сохранённый в ней адрес становится недействительным; простое оставление этого адреса привело бы к обращению к освобождённой памяти.
Наивный перебор всех объектов и всех ссылок процесса был бы слишком дорогим. Кроме того, runtime должен корректно обрабатывать создание, копирование, чтение и удаление слабых ссылок во время многопоточного выполнения.
При создании weak-ссылки runtime регистрирует её в служебной структуре, связанной с объектом. В реализациях Swift это обычно описывают как таблицу слабых ссылок или side table, но точная внутренняя организация является деталью реализации и не гарантируется языком.
Когда последний владелец освобождает объект и объект действительно переходит к уничтожению, runtime находит зарегистрированные weak-ссылки, делает их nil, а затем завершает освобождение объекта. Поэтому чтение weak-ссылки после уничтожения возвращает nil, а не прежний адрес.
Служебный учёт имеет цену: weak обычно сложнее и дороже сильной ссылки по памяти и времени. Это одна из причин, по которой weak применяют для не владеющих связей, а не используют без необходимости.
Важно отличать это от unowned. Невладеющая ссылка не регистрируется как обнуляемая weak-ссылка: после уничтожения объекта она не превращается в nil, поэтому обращение к ней может завершиться ошибкой выполнения.
В приложении экран создаёт объект модели, а десятки временных наблюдателей хранят weak-ссылки на эту модель. После закрытия экрана сильные ссылки исчезают, и модель уничтожается.
Вариант с сильными ссылками прост, но создаёт риск, что наблюдатели будут удерживать модель дольше необходимого или образуют цикл. Вариант с unowned дешевле, однако требует строгой гарантии, что каждый наблюдатель исчезнет раньше модели; ошибка даст обращение к уничтоженному объекту.
Выбор weak оправдан, если наблюдатели могут пережить модель. Runtime обнулит все зарегистрированные ссылки, после чего они безопасно перестанут выполнять действия. Результат — отсутствие retain cycle и отсутствие обращения к освобождённой памяти, ценой дополнительного служебного учёта.
Обнуляет ли ARC weak-ссылки немедленно при достижении нулевого числа сильных ссылок?
Не следует смешивать логические этапы. Когда объект становится доступным для уничтожения, runtime выполняет необходимую процедуру разрушения и обнуления weak-ссылок; точный момент относительно оптимизаций, deinit и внутренних операций не следует выводить из исходной области видимости переменной. Гарантируется безопасная семантика weak-ссылки, а не конкретная трассировка retain/release.
Гарантирует ли weak-ссылка, что объект останется жив во время последующего использования результата чтения?
При чтении weak-ссылки runtime получает временное сильное значение. Это позволяет безопасно использовать полученный результат в рамках соответствующего выражения или временного удержания. Но отдельные последовательные чтения weak-ссылки могут дать разные результаты: между ними объект способен быть уничтожен.
Можно ли заменить weak на unowned только ради уменьшения накладных расходов?
Только если жизненный цикл гарантирован: объект, на который ссылаются, обязан жить не меньше, чем каждая unowned-ссылка. unowned не участвует в удержании объекта и не обнуляется при его уничтожении, поэтому нарушение гарантии превращается в runtime-ошибку. Если такая гарантия не доказуема, безопаснее использовать weak и обработать возможное отсутствие объекта.