Где установить кэш изображений при использовании объединения кадров - PullRequest
0 голосов
/ 26 марта 2020

Обучение кешированию изображения с помощью 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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...