Обучение кешированию изображения с помощью NSCache в YouTube.
Ниже приведен пример кода, который работает хорошо. Я мог видеть, что он устанавливает изображение в кэш в методе getImageFromResponse
после того, как данные возвращаются из URLSession.shared.dataTask
. Ну, это старый способ использования обработчика завершения @escaping.
import Foundation
import SwiftUI
class UrlImageModel: ObservableObject {
@Published var image: UIImage?
var urlString: String?
var imageCache = ImageCache.getImageCache()
init(urlString: String?) {
self.urlString = urlString
loadImage()
}
func loadImage() {
if loadImageFromCache() {
print("Cache hit")
return
}
print("Cache miss, loading from url")
loadImageFromUrl()
}
func loadImageFromCache() -> Bool {
guard let urlString = urlString else {
return false
}
guard let cacheImage = imageCache.get(forKey: urlString) else {
return false
}
image = cacheImage
return true
}
func loadImageFromUrl() {
guard let urlString = urlString else {
return
}
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url, completionHandler: getImageFromResponse(data:response:error:))
task.resume()
}
func getImageFromResponse(data: Data?, response: URLResponse?, error: Error?) {
guard error == nil else {
print("Error: \(error!)")
return
}
guard let data = data else {
print("No data found")
return
}
DispatchQueue.main.async {
guard let loadedImage = UIImage(data: data) else {
return
}
self.imageCache.set(forKey: self.urlString!, image: loadedImage)
self.image = loadedImage
}
}
}
class ImageCache {
var cache = NSCache<NSString, UIImage>()
func get(forKey: String) -> UIImage? {
return cache.object(forKey: NSString(string: forKey))
}
func set(forKey: String, image: UIImage) {
cache.setObject(image, forKey: NSString(string: forKey))
}
}
extension ImageCache {
private static var imageCache = ImageCache()
static func getImageCache() -> ImageCache {
return imageCache
}
}
И мое приложение использует последнюю платформу Combine для загрузки изображений. Я пытаюсь добавить строку в loadImageFromUrl()
, я думаю, что после assign(to: \.image, on: self)
значение image
должно иметь значение. Однако консоль выдала мне сообщение об ошибке «Неустранимая ошибка: неожиданно обнаружил ноль при развертывании необязательного значения: файл / Пользователи / Чак / Документы / iOS / Проекты / Моменты / Моменты / Модель / Url Image / UrlImageModel.swift, строка 55».
Итак, используя нативный NSCache, как или где установить кэш изображений при объединении сетей? Ниже мой код, пожалуйста, дайте мне подсказку!
import Foundation
import SwiftUI
import Combine
class UrlImageModel: ObservableObject {
@Published var image: UIImage?
private var url: URL?
private var cancellable: AnyCancellable?
private var imageCache = ImageCache.getImageCache()
init(url: URL?) {
self.url = url
loadImage()
}
func loadImage() {
if loadImageFromCache() {
print("Cache hit")
return
}
print("Cache missing, loading from url")
loadImageFromUrl()
}
func loadImageFromCache() -> Bool {
guard let url = url else {
return false
}
guard let cacheImage = imageCache.get(key: url) else {
return false
}
image = cacheImage
return true
}
func loadImageFromUrl() {
cancellable = URLSession.shared.dataTaskPublisher(for: url!)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.receive(on: DispatchQueue.main)
.assign(to: \.image, on: self)
// set image as cache!!
imageCache.set(key: url!, image: image!)
}
func cancel() {
cancellable?.cancel()
}
}
class ImageCache {
var cache = NSCache<NSURL, UIImage>()
func get(key: URL) -> UIImage? {
return cache.object(forKey: key as NSURL)
}
func set(key: URL, image: UIImage) {
cache.setObject(image, forKey: key as NSURL)
}
}
extension ImageCache {
private static var imageCache = ImageCache()
static func getImageCache() -> ImageCache {
return imageCache
}
}
/// Обновление, я нашел, как это сделать. Добавьте метод Combine handleEvents
и установите кэш изображений в закрытии receiveOutput, чтобы устранить проблему. Кстати, есть больше параметров, которые имеют дело с вышестоящим издателем и нисходящим получателем. Для меня это требует больше практики, чтобы использовать этот новый способ создания сетей в Combine.
Фиксированный код , я также добавляю индекс в часть кэша изображений.
import Foundation
import SwiftUI
import Combine
class UrlImageModel: ObservableObject {
@Published var image: UIImage?
private var url: URL?
private var cancellable: AnyCancellable?
private var imageCache = ImageCache.getImageCache()
init(url: URL?) {
self.url = url
loadImage()
}
func loadImage() {
if loadImageFromCache() {
print("Cache hit")
return
}
print("Cache missing, loading from url")
loadImageFromUrl()
}
func loadImageFromCache() -> Bool {
guard let url = url else {
return false
}
guard let cacheImage = imageCache[url] else {
return false
}
image = cacheImage
return true
}
func loadImageFromUrl() {
guard let url = url else {
return
}
cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
// set image into cache!
.handleEvents(receiveOutput: { [unowned self] image in
guard let image = image else {return}
self.imageCache[url] = image
})
.receive(on: DispatchQueue.main)
.assign(to: \.image, on: self)
}
}
class ImageCache {
var cache = NSCache<NSURL, UIImage>()
subscript(_ key: URL) -> UIImage? {
get { cache.object(forKey: key as NSURL) }
set { newValue == nil ? cache.removeObject(forKey: key as NSURL) : cache.setObject(newValue!, forKey: key as NSURL) }
}
}
extension ImageCache {
private static var imageCache = ImageCache()
static func getImageCache() -> ImageCache {
return imageCache
}
}