В кэше объект хранится только через weak-ссылку. Почему он может исчезнуть сразу после добавления в кэш?
weak-ссылка не владеет объектом, поэтому не увеличивает его счётчик сильных ссылок. Если после добавления в кэш не осталось других сильных ссылок, объект уничтожается, а weak-ссылка автоматически становится nil.
Такой кэш хранит объект только пока его жизнью управляет внешний владелец. Это позволяет кэшу не продлевать время жизни данных, но требует корректно обрабатывать промахи кэша.
ARC автоматизировал управление сильными ссылками, заменив ручное управление подсчёком ссылок. При этом сохранилась необходимость описывать отношения, которые не должны владеть объектом: например, делегаты, обратные ссылки и кэши.
Для таких отношений используются weak-ссылки. Их задача — не удерживать объект в памяти и автоматически обнуляться после его уничтожения, предотвращая висячие ссылки.
Предположим, кэш сохранил объект только через weak. Разработчик может ошибочно считать сам факт наличия записи в кэше гарантией того, что объект доступен.
На самом деле объект может быть уничтожен сразу после исчезновения последней сильной ссылки. В результате запись в кэше останется, но её значение будет nil; это нормальный промах кэша, а не ошибка ARC.
Сильная ссылка увеличивает число владений объектом. weak-ссылка владением не является: она лишь регистрируется runtime как ссылка, которую нужно обнулить при уничтожении объекта.
Последовательность событий такова:
weak-ссылку, не добавляя владение.WeakBox остаётся в словаре, но его value становится nil. Поэтому при чтении кэша нужно проверять значение и удалять устаревшую запись либо создавать объект заново.
Преимущество такого подхода — кэш не вызывает утечек памяти и не удерживает редко используемые объекты. Компромисс — содержимое кэша не гарантировано: объект может исчезнуть между двумя операциями, если код не владеет им сильной ссылкой.
Если кэш должен гарантировать наличие объекта, он должен хранить его сильной ссылкой или использовать отдельную стратегию вытеснения. Сильный кэш надёжнее с точки зрения доступности, но способен существенно увеличить потребление памяти.
В приложении изображения хранятся в кэше экранов. При использовании сильных ссылок изображения остаются в памяти после закрытия экранов, потому что кэш продолжает ими владеть. Это приводит к росту памяти и возможному аварийному завершению при нехватке памяти.
Рассматривались два варианта:
Для вторичного кэша выбран слабый вариант. Код сначала проверяет weak-значение, а при nil загружает или создаёт объект повторно. Результат — кэш ускоряет повторное использование объектов, но не становится скрытым владельцем всей коллекции.
Нет. Запись подтверждает только наличие контейнера или обёртки, но не наличие объекта. В любой момент после освобождения последней сильной ссылки значение weak-ссылки может стать nil, поэтому результат чтения нужно обрабатывать как необязательный.
Чтение weak-ссылки даёт доступ к объекту, но сам кэш не становится его владельцем. Если объект используется в нескольких шагах, следует получить сильную локальную ссылку и работать с ней в рамках операции; это не даёт объекту исчезнуть из-за отсутствия владельца во время этой операции.
Такая локальная ссылка действует только до окончания своего времени жизни. После её освобождения объект снова может быть уничтожен, если других сильных владельцев нет.
Ограниченный сильный кэш сам владеет объектами и удаляет их по политике, например при превышении размера или по принципу наименее недавно использованных элементов. Weak-кэш не владеет объектами вообще, поэтому они исчезают независимо от размера кэша.
Сильный кэш даёт предсказуемую доступность до момента вытеснения, а weak-кэш экономит память, но допускает неожиданные промахи. Выбор зависит от того, важнее ли гарантированное повторное использование или минимальное удержание объектов в памяти.