Ответ
Эффективное кеширование изображений включает два уровня: оперативная память (RAM) и дисковое хранилище. Это ускоряет повторное отображение и снижает нагрузку на сеть.
Базовая реализация с NSCache (кеш в памяти):
class ImageCache {
static let shared = ImageCache()
private let memoryCache = NSCache<NSString, UIImage>()
func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
let cacheKey = url.absoluteString as NSString
// 1. Проверка кеша в памяти
if let cachedImage = memoryCache.object(forKey: cacheKey) {
completion(cachedImage)
return
}
// 2. Загрузка из сети, если нет в кеше
URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
guard let self = self,
let data = data,
let image = UIImage(data: data),
error == nil else {
completion(nil)
return
}
// 3. Сохранение в кеш памяти
self.memoryCache.setObject(image, forKey: cacheKey)
DispatchQueue.main.async {
completion(image)
}
}.resume()
}
}
Улучшения для production:
- Двухуровневое кеширование: Добавьте дисковое кеширование с помощью
FileManager. Сначала проверяйте RAM, затем диск, и только потом сеть. - Инвалидация кеша: Реализуйте политику очистки (например, по времени или размеру кеша).
NSCacheавтоматически очищает память при нехватке, но для диска нужна своя логика. - Очередь операций: Добавьте очередь или механизм отслеживания загрузок, чтобы избежать повторной загрузки одного изображения несколькими запросами одновременно.
- Использование готовых решений: Для большинства проектов лучше использовать оптимизированные библиотеки, такие как SDWebImage или Kingfisher, которые решают эти проблемы "из коробки".
Ответ 18+ 🔞
А, ну вот, опять про картинки! Ну слушай, народ вечно изобретает велосипед, когда можно просто взять и не ебать себе мозг. Но раз уж спросили — сейчас разжуём, как эти ваши фотки котиков не грузить по сто раз на дню.
Так, смотри. Картинка должна появляться быстро, а не как у старого модема — пиликать минуту. Для этого её надо прятать в двух местах: в оперативке (быстро, но мало) и на диске (медленнее, но надёжно). Как водка в холодильнике и про запас в кладовке, понимаешь?
Вот тебе базовый пример, как сделать первый склад — в оперативке. Смотри, не зевай:
class ImageCache {
static let shared = ImageCache()
private let memoryCache = NSCache<NSString, UIImage>()
func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
let cacheKey = url.absoluteString as NSString
// 1. Проверка кеша в памяти
if let cachedImage = memoryCache.object(forKey: cacheKey) {
completion(cachedImage)
return
}
// 2. Загрузка из сети, если нет в кеше
URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
guard let self = self,
let data = data,
let image = UIImage(data: data),
error == nil else {
completion(nil)
return
}
// 3. Сохранение в кеш памяти
self.memoryCache.setObject(image, forKey: cacheKey)
DispatchQueue.main.async {
completion(image)
}
}.resume()
}
}
Видишь? Проверили на полке в оперативке — нет? Ну тогда пошли в интернет-магазин, купили, и сразу на полочку положили. В следующий раз уже с полки возьмём. Элементарно, Ватсон!
Но это, блядь, детский сад, штаны на лямках. Для настоящего прожжённого проекта надо делать по-взрослому, а то потом волосы на жопе выдирать будешь.
Что ещё надо доделать, чтобы не облажаться:
-
Двухуровневое кеширование, ёпта! Твой
NSCache— это как холодильник: вырубили свет, и всё, твои пельмени протухли. Надо второй склад — на диск, черезFileManager. Порядок проверки: сначала шаримся в оперативке, потом копаемся в папках на диске, и только если нихуя не нашли — идём грузить по сети. Как в жизни: сначала в карманах, потом в тумбочке, потом уже идёшь в магазин. -
Инвалидация кеша, или когда пора выкидывать хлам. Оперативка сама почистится, когда память кончится —
NSCacheумный. А вот файлы на диске будут лежать, пока ты их вручную не сотрёшь, как старые журналы «Плейбой» на антресолях. Надо придумать политику: удалять то, что старше недели, или следить, чтобы папка не раздулась больше гигабайта. А то пользователь потом удивляется: «Чё это у меня память кончилась?». -
Очередь операций, чтобы не нагружать всё вхолостую. Представь: десять ячеек таблицы просят одну и ту же картинку. Твой наивный код запустит десять одинаковых сетевых запросов! Это же пиздец, а не оптимизация. Надо ставить загрузку в очередь и следить, если уже качаем эту ссылку — просто подписываем нового слушателя на результат, а не стартуем новую задачу.
-
Использование готовых решений, потому что нехуй страдать ерундой. Серьёзно, чувак. Ты что, думаешь, ты первый, кто это придумал? Возьми SDWebImage или Kingfisher, установи через CocoaPods, и забудь как страшный сон. Там уже всё сделано: и два уровня кеша, и очередь, и анимации при загрузке, и индикаторы. Тратить неделю на то, что можно подключить за пять минут — это, прости, мартышлюшка.
Короче, суть ясна? Сделать просто — легко. Сделать хорошо, чтобы не развалилось через месяц у тысяч пользователей — тут уже надо голову включать. А лучше — голову не включать, а взять готовую библиотеку и заняться чем-то действительно полезным. Ну, или хотя бы кофе попить.