当前位置: 首页 > 知识库问答 >
问题:

我试图为UIImageView添加一个扩展以异步加载图像,但它抛出了一个错误。迅捷

昝存
2023-03-14

由于声明了一个常数而在第3行出现错误,为什么会出现这种情况?错误:扩展名不能包含存储的属性代码:

extension UIImageView {

    let imageCache = NSCache<NSString, UIImage>() //error
    
        func imageFromServerURL(_ URLString: String, placeHolder: UIImage?) {

        self.image = nil
        //If imageurl's imagename has space then this line going to work for this
        let imageServerUrl = URLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
        if let cachedImage = imageCache.object(forKey: NSString(string: imageServerUrl)) {
            self.image = cachedImage
            return
        }

        if let url = URL(string: imageServerUrl) {
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

                //print("RESPONSE FROM API: \(response)")
                if error != nil {
                    print("ERROR LOADING IMAGES FROM URL: \(String(describing: error))")
                    DispatchQueue.main.async {
                        self.image = placeHolder
                    }
                    return
                }
                DispatchQueue.main.async {
                    if let data = data {
                        if let downloadedImage = UIImage(data: data) {
                            self.imageCache.setObject(downloadedImage, forKey: NSString(string: imageServerUrl))
                            self.image = downloadedImage
                        }
                    }
                }
            }).resume()
        }
    }
}

我使用了这段代码,但不明白为什么它不起作用:Swift异步加载图像

共有2个答案

易镜
2023-03-14

是,扩展可能不会添加存储的属性。这可能是最好的方法,这样您的类不会因为库而增加大小,因为库通过添加一些额外的信息来扩展您的类,而这些信息对您来说很可能是不感兴趣的。

但在您的情况下,您可能甚至不想拥有一个存储的财产。您的编码方式意味着您的每个UIIimageView实例都有自己的缓存。因此,例如,每个单元格显示图像视图的表视图意味着每个可见单元格的缓存,这导致同一图像的多次下载,而不是共享它。

在您的情况下,解决此问题的最佳方案可能是使其静态化:

extension UIImageView {

    private static let imageCache = NSCache<NSString, UIImage>()
    private var imageCache: NSCache<NSString, UIImage> { UIImageView.imageCache }
    
    func imageFromServerURL(_ URLString: String, placeHolder: UIImage?) {
        self.image = nil
        // If image url's image name has space then this line going to work for this
        let imageServerUrl = URLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
        if let cachedImage = imageCache.object(forKey: NSString(string: imageServerUrl)) {
            self.image = cachedImage
            return
        }

        if let url = URL(string: imageServerUrl) {
            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
                //print("RESPONSE FROM API: \(response)")
                if error != nil {
                    print("ERROR LOADING IMAGES FROM URL: \(String(describing: error))")
                    DispatchQueue.main.async {
                        self.image = placeHolder
                    }
                    return
                }
                DispatchQueue.main.async {
                    if let data = data {
                        if let downloadedImage = UIImage(data: data) {
                            self.imageCache.setObject(downloadedImage, forKey: NSString(string: imageServerUrl))
                            self.image = downloadedImage
                        }
                    }
                }
            }).resume()
        }
    }
    
}

撇开你的问题不谈,你应该知道你试图解决的任务可能没有你提供的代码那么容易。我在您的代码中看到至少有2个主要问题:

  1. 当快速多次调用此方法时,您可能会出现一个争用条件,即第一个调用下载映像的时间将晚于第二个调用。在这种情况下,将显示第一个图像,而不是第二个(最后一个)图像
  2. 对同一图像多次调用此方法时,您将在同一资源上多次启动下载(同一图像显示在屏幕的两个位置上,将下载它两次)

而且可能还有更多的。例如,这都在内存中。在内存耗尽之前,您希望可以缓存多少映像?也许最好将它移到某个文件系统中。

李安歌
2023-03-14

As扩展不能包含存储的属性,因此您可以将其设为全局变量,并从self.imageCache.setObject(downloadedImage,forkey:NSString(string:imageServerUrl))中删除self

let imageCache = NSCache<NSString, UIImage>() 

class SubImg: UIImageView {}
 类似资料: