Как реализовать кэширование в Swift
Что проверяют
Знаете ли вы встроенные механизмы кэширования в iOS и умеете ли выбрать уровень: память, диск или HTTP-кэш. Следом обычно спрашивают, как ваш кэш ведёт себя при нехватке памяти и при обращении из нескольких потоков.
Как отвечать
Разложите ответ по уровням:
- память,
NSCache: потокобезопасен, сам вытесняет объекты при нехватке памяти, лимиты задаются черезcountLimitиtotalCostLimit, а стоимость передаётся вsetObject(_:forKey:cost:). Ключи и значения должны быть классами, поэтомуStringоборачивают вNSString, а структуры в класс-обёртку. Лимиты не жёсткие: система может выбросить объект раньше; - свой кэш в памяти: словарь внутри
actor, если нужен предсказуемый LRU. Актор снимает вопрос гонок в Swift Concurrency; - HTTP-кэш,
URLCache:URLSessionсам кэширует ответы поCache-ControlиETag. Размер задаётся вURLCache(memoryCapacity:diskCapacity:directory:), поведение запроса черезcachePolicy. Для сетевых ответов это часто лучше самописного слоя; - диск: файлы в каталоге
.cachesDirectoryчерезFileManager. Система может очистить его при нехватке места, так что приложение должно переживать пропажу файлов; - картинки: Kingfisher, SDWebImage или Nuke дают двухуровневый кэш, даунсэмплинг и отмену загрузки при переиспользовании ячейки.
Добавьте про инвалидацию: TTL, версия в ключе, сброс кэша при выходе пользователя из аккаунта.
Где спотыкаются
Кладут кэш в UserDefaults. Он для настроек: читается целиком и не рассчитан на мегабайты картинок и ответов.
Используют обычный словарь из нескольких потоков без синхронизации и ловят падения. NSCache или актор решают это без ручных блокировок.
Кладут в кэш полноразмерные UIImage без даунсэмплинга. Фото 4000 на 3000 пикселей в декодированном виде занимает около 48 МБ, сколько бы ни весил файл, и приложение выгружают по памяти.