从 Swift 3 中的 URL 列表下载内容的安全方法

3 loops asynchronous while-loop completionhandler swift

我有一个提供图像资源位置的 URL 列表。我设法找到了解决方案,但我觉得似乎有比下面代码中显示的过程更好的方法。非常感谢有关如何改进此方法以异步检索图像的任何提示!

在我附加每个项目之后调用completionHandler,并在异步块之外将1添加到索引(i),这意味着while循环在最后一个url被完全处理之前迭代到下一个url项目,这不是很奇怪吗?

typealias imagesHandler = (_ images: [UIImage]) -> Void

func fetchImages(forUser user: User, completionHandler: imagesHandler?) {

    var images = [UIImage]()

    self.fetchImageUrlList(forUser: user) { (urlList: [URL]) in

        var i = 0

        while i <= urlList.count - 1 {
            let request = URLRequest(url: urlList[i])
            let dataTask = URLSession(configuration: .default).dataTask(with: request, completionHandler: {
                (data: Data?, response: URLResponse?, error: Error?) in
                guard error == nil else { return }
                guard let data = data else { return }
                guard let image = UIImage(data: data) else { return }

                images.append(image)
                completionHandler?(Array(Set(images)))
            })
            i += 1
            dataTask.resume()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Luc*_*tti 5

大中央快讯

这是一个典型的多线程场景,Grand Central Dispatch 非常有帮助。

首先,这是您更新的代码,以便使用 Grand Central Dispatch

func fetchImages(forUser user: User, completionHandler: ImagesHandler?) {

    var images = [UIImage]()
    let group = DispatchGroup()
    let serialQueue = DispatchQueue(label: "serialQueue")

    fetchImageUrlList(forUser: user) { urls in

        urls.forEach { url in
            // ***********************************************
            // tells the group that there is a pending process
            group.enter()
            URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in

                guard let data = data, let image = UIImage(data: data), error == nil else { group.leave(); return }

                // ***************************************************************************
                // creates a synchronized access to the images array
                serialQueue.async {
                    images.append(image)

                    // ****************************************************
                    // tells the group a pending process has been completed
                    group.leave()
                }
            }.resume()
        }

        group.notify(queue: .main) {
            // *****************************************************************************************
            // this will be executed when for each group.enter() call, a group.leave() has been executed
            completionHandler?(images)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

集团调度

用这条线我创建了一个GroupDispatch

let group = DispatchGroup()
Run Code Online (Sandbox Code Playgroud)

您可以将其视为计数器。每次调用group.enter()计数器都会增加1。每次调用group.leave()计数器都会减少1

当然DispatchGroup线程安全的

最后你写的一切

group.notify(queue: .main) {
    // HERE <------
}
Run Code Online (Sandbox Code Playgroud)

当 的内部计数器为 时将被DispatchGroup执行zero。事实上,这意味着所有待处理的“操作”都已完成。

串行队列

最后,您需要images从不同的线程访问该数组。这是非常危险的。

所以我们使用串行队列。

serialQueue.async {
    images.append(image)
    group.leave()
}
Run Code Online (Sandbox Code Playgroud)

我们附加到 a 的所有闭包Serial Queue当时都被执行 1。所以images数组是安全访问的。

我还没有测试过代码,请尝试告诉我;)