Swift

时间:2018-04-19 05:07:07

标签: swift grand-central-dispatch dispatch dispatch-async

我对编程很新,所以我有一个简单的问题。实际上,写作有助于我更快地发现问题。

无论如何,我有一个带有多个异步调用的应用程序,它们嵌套如下:

InstagramUnoficialAPI.shared.getUserId(from: username, success: { (userId) in

    InstagramUnoficialAPI.shared.fetchRecentMedia(from: userId, success: { (data) in

        InstagramUnoficialAPI.shared.parseMediaJSON(from: data, success: { (media) in

                guard let items = media.items else { return }
                self.sortMediaToCategories(media: items, success: {

                    print("success")

          // Error Handlers

看起来很可怕,但那不是重点。一旦我开始工作,我将调查Promise Kit。

我需要sortMediaToCategories等待完成,然后重新加载我的集合视图。但是,在sortMediaToCategories我有另一个嵌套函数,它也是异步的并且有一个for循环。

func sortMediaToCategories(media items: [StoryData.Items],
                           success: @escaping (() -> Swift.Void),
                           failure: @escaping (() -> Swift.Void)) {

    let group = DispatchGroup()
    group.enter()


    for item in items {


        if item.media_type == 1 {

            guard let url = URL(string: (item.image_versions2?.candidates?.first!.url)!) else {return}

            mediaToStorageDistribution(withImageUrl: url,
                                       videoUrl: nil,
                                       mediaType: .jpg,
                                       takenAt: item.taken_at,
                                       success: { group.notify(queue: .global(), execute: {

                                        self.collectionView.reloadData()
                                           group.leave()

                                       })  },
                                       failure: { print("error") })

 //....

我无法承受集合视图显然每次重新加载,所以我需要等待循环完成然后重新加载。

我正在尝试使用Dispatch Groups,但正在努力解决它。你能帮帮我吗?任何简单的例子和​​任何建议都将非常感激。

3 个答案:

答案 0 :(得分:5)

您遇到的问题很常见:​​拥有多个异步任务并等待所有任务完成。

有一些解决方案。最简单的就是使用DispatchGroup

func loadUrls(urls: [URL], completion: @escaping ()->()) {
    let grp = DispatchGroup()

    urls.forEach { (url) in
        grp.enter()
        URLSession.shared.dataTask(with: url) { data, response, error in
            // handle error
            // handle response
            grp.leave()
        }.resume()
    }

    grp.notify(queue: DispatchQueue.main) {
        completion()
    }
}

函数loadUrls是异步的,并且期望一个URL数组作为输入,并且在完成所有任务时将调用一个完成处理程序。这将通过所示的DispatchGroup来完成。

关键是,确保在调用任务之前调用 ,并在任务完成时调用grp.enter() 。 <{1}}和grp.leave应保持平衡。

enter最终注册一个闭包,当DispatchGroup leave平衡时(即其内部计数器达到零),将在指定的调度队列(此处为:main)上调用该闭包。

但是,这个解决方案有一些注意事项:

  • 所有任务将几乎同时启动并同时运行
  • 此处未显示通过完成处理程序报告所有任务的最终结果。它的实现需要适当的同步。

对于所有这些警告,有很好的解决方案应该使用合适的第三方库来实现。例如,您可以将任务提交给某种&#34;执行者&#34;它控制并发运行的任务数量(匹配OperationQueue和async Operations)。

许多&#34;承诺&#34;或&#34;未来&#34;库简化了错误处理,并且只需一次函数调用就可以帮助您解决这些问题。

答案 1 :(得分:1)

当最后一个项目以这种方式调用成功块时,您可以reloadData

let lastItemIndex = items.count - 1

for(index, item) in items.enumerated() {

if item.media_type == 1 {

  guard let url = URL(string: (item.image_versions2?.candidates?.first!.url)!) else {return}

  mediaToStorageDistribution(withImageUrl: url,
                             videoUrl: nil,
                             mediaType: .jpg,
                             takenAt: item.taken_at,
                             success: { 
                               if index == lastItemIndex {
                                  DispatchQueue.global().async {

                                    self.collectionView.reloadData()
                                  }         
                                } 
                             },
                             failure: { print("error") })
  }

答案 2 :(得分:1)

您必须在循环内移动group.enter()调用。必须平衡进入和离开的呼叫。如果您成功和失败的mediaToStorageDistribution函数的回调是独占的,您还需要让该组失败。当所有调用enter的块离开时,将调用group notify。并且您可能希望用break替换guard语句中的return,以便跳过缺少URL的项目。现在你从整个sortMediaToCatgories函数返回。

{{1}}