【问题标题】:How to retrieve array of images from array of url swift如何从 url swift 数组中检索图像数组
【发布时间】:2021-01-02 17:43:16
【问题描述】:

我正在尝试从 url 数组中检索图像..

我有这个功能与我不会做的一样,但它不起作用,所以我尝试使用 URLSession 但不知道如何实现它>>

func downloadImages(imageUrls: [String], completion: @escaping (_ images: [UIImage?]) -> Void) {
        
    var imageArray: [UIImage] = []
    var downloadCounter = 0
    
    for link in imageUrls {
        let url = NSURL(string: link)
        let  downloadQueue = DispatchQueue(label: "imageDowmloadQueue")
        
        downloadQueue.sync {
            downloadCounter += 1
            let data = NSData(contentsOf: url! as URL)
            
            if data != nil {
                //image data ready and need to be converted to UIImage
                imageArray.append(UIImage(data: data! as Data)!)
                
                if downloadCounter == imageArray.count {
                    DispatchQueue.main.async {
                        completion(imageArray)
                        
                    }
                }
            } else {
                print("couldnt download image")
                completion(imageArray)
            }
        }
    }
}

我工作的功能:

public func imagesFromURL(urlString: [String],completion: @escaping (_ images: [UIImage?]) -> Void) {
        
        var imageArray: [UIImage] = []
        var downloadCounter = 0
        let  downloadQueue = DispatchQueue(label: "imageDowmloadQueue")

        for link in urlString {
            downloadQueue.sync {
            downloadCounter += 1
        let dataTask = URLSession.shared.dataTask(with: NSURL(string: link)! as URL, completionHandler: { (data, response, error ) in

            if error != nil {
                print(error ?? "No Error")
                return
            }
            
            if data != nil {
                imageArray.append(UIImage(data: data! as Data)!)
                if downloadCounter == imageArray.count {
                    completion(imageArray)

                    }
                } else {
                            print("couldnt download image")
                            completion(imageArray)
                           }
                
            } dataTask.resume()
                }
            }
}

我想调用集合单元格中的函数并仅从每个艺术品数组中获取显示的第一张图像..


        //download the first image only to display it:
        if artwork.ImgLink != nil && artwork.ImgLink.count > 0 {
            
            downloadImages(imageUrls: [artwork.ImgLink.first!]) { (images) in
                self.artworkImage.image = images.first as? UIImage
            }
        }

【问题讨论】:

    标签: arrays swift nsurlsession nsurlsessiondownloadtask


    【解决方案1】:
    1. 如果您打算只使用 URL 数组中第一个可用的UIImage,则不要设计一个尝试下载所有这些的函数。相反,尝试从第一个 url 下载,如果成功则返回下载的 UIImage,如果失败则继续使用第二个 url,重复直到获得 UIImage
    2. 在本地函数中创建DispatchQueue 对我来说看起来很危险。更常见的做法是在其他地方维护一个队列并将其作为参数传递给函数,或者如果您没有特定原因,则使用 DispatchQueue.global(qos:) 重用预定义的全局队列之一。
    3. 小心syncsync 阻塞调用线程,直到您的块在队列中完成。一般你使用async
    4. 使用Int 计数器来控制何时完成多个异步任务(何时调用完成块)可以工作,但可以通过使用DispatchGroup 来改进,它以简单明了的方式处理多个异步任务。李>

    这里有两个函数。两者都有效。 firstImage(inURLs:completion:) 只返回它下载成功的第一个UIImageimages(forURLs:completion:) 尝试下载并全部返回。

    func firstImage(inURLs urls: [String], completion: @escaping (UIImage?) -> Void) {
        DispatchQueue.global().async {
            for urlString in urls {
                if let url = URL(string: urlString) {
                    if let data = try? Data(contentsOf: url), let image = UIImage(data: data) {
                        DispatchQueue.main.async {
                            completion(image)
                        }
                        return
                    }
                }
            }
            DispatchQueue.main.async {
                completion(nil)
            }
        }
    }
    
    // Use it.
    firstImage(inURLs: artwork.ImgLink) { image in
        self.artworkImage.image = image
    }
    
    
    func images(forURLs urls: [String], completion: @escaping ([UIImage?]) -> Void) {
        let group = DispatchGroup()
        var images: [UIImage?] = .init(repeating: nil, count: urls.count)
        
        for (index, urlString) in urls.enumerated() {
            group.enter()
            DispatchQueue.global().async {
                var image: UIImage?
                if let url = URL(string: urlString) {
                    if let data = try? Data(contentsOf: url) {
                        image = UIImage(data: data)
                    }
                }
                images[index] = image
                group.leave()
            }
        }
        
        group.notify(queue: .main) {
            completion(images)
        }
    }
    
    // Use it.
    images(forURLs: artwork.ImgLink) { images in
        self.artworkImage.image = images.first(where: { $0 != nil }) ?? nil
    }
    

    【讨论】:

    • 不要使用 Data(contentsOf: url) 从非本地资源/甚至从后台线程加载数据。
    • @LeoDabus 你能详细说明一下吗? Apple 建议不要将 Data(contentsOf:) 用于远程 url,因为它会阻塞当前线程:developer.apple.com/documentation/foundation/nsdata/…。阻止从后台线程使用它的原因是什么?
    • 您将同步初始化程序用于错误目的。使用 URLSession 方法,它们是异步请求的正确方法。相关stackoverflow.com/a/3078783/2303865
    • 仍然没有回答具体问题“阻止从后台线程使用它的原因是什么?”。 URLSession 确实适用于异步请求(数据或文件),但是无需额外努力,它似乎与在后台队列中使用 Data(contentOf:) 没有显着区别。它们都将图像一次提取到内存中没有任何优化,即解码、缓存、失效。然而,优化是一个更深层次的问题,超出了这篇文章的范围,并且有一些库解决了这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-09
    • 1970-01-01
    • 2019-04-27
    • 1970-01-01
    相关资源
    最近更新 更多