【问题标题】:Completion block method vs. DispatchQueue完成块方法与 DispatchQueue
【发布时间】:2018-06-20 04:23:10
【问题描述】:

我已经实现了以下完成块,一个块完成,然后我相应地更新 UI 和对象。

func doPaging() {
    fetchProducts(page: pageNumber , completion: { success in
        if let products = success as? Products
        {
            DispatchQueue.main.async {
                self.products.append(contentsOf:products)
                self.isWating = false;
                self.productTableView.reloadData()
            }
        }
    })
} 

func fetchProducts(page: Int, completion: @escaping ((AnyObject) -> Void)) {

   // URLSession call here
}

但是,以下方法清楚地表明,restful 调用将发生在后台线程中,一旦完成,然后更新 UI 和对象。

func doPaging() {

    DispatchQueue.global(qos: .background).async {
        // Background Thread
        fetchProducts()
        DispatchQueue.main.async {
            self.pageNumber += 1
            self.productTableView.reloadData()
            self.isWating = false            
        }
    }
}

func fetchProducts(page: Int) {

   // URLSession call here
}

我在完成块方法和 DispatchQueue 之间感到困惑。

推荐哪一个?

【问题讨论】:

  • 你的两个doPaging 方法根本不相似。第一个调用fetchProducts,而第二个没有。
  • @rmaddy,添加了缺失的部分。
  • 两种方法都试过了吗?请注意事物执行顺序的巨大差异。了解此类事情的最佳方法是尝试。实验。在这种情况下,您将通过实验而不是发布问题学到更多。一旦你得到你不太理解的结果,然后发布一个问题要求澄清。
  • 如果fetchProducts 是异步的,则使用第一个选项。如果fetchProducts 与第二个同步。

标签: ios swift asynchronous


【解决方案1】:

在第一种方法中,您调用一个内部使用 NSURLSession 的方法 fetchProducts()。使用 NSURLSession 的 REST 调用在后台运行,在 REST 调用完成后,将调用任务的完成。在该完成中,您调用 fetchProducts() 的完成处理程序。这种方法对我来说似乎很好。

在第二种方法中,您使用全局后台队列并异步调用 NSURLSession API(我假设是这样),并且不要等待调用完成。主队列上的代码将立即被调用,此时 NSURLSession 任务可能已经完成,也可能没有。 所以,这种方法是有问题的。

【讨论】:

  • "NSURLSession 任务可能已经完成,也可能没有完成" - URLSession 甚至还没有开始。
【解决方案2】:

只要您异步获取产品,第一种方法似乎没问题。在 fetchProducts() 中,如果您在主队列中调用完成块,您甚至不需要在 doPaging() 方法中再次获取主队列。

在您的第二种方法中,您在全局(并发)队列中调用 fetchProducts()。尽管全局队列按照添加到队列中的顺序启动每个任务,但它们同时运行任务。由于 fechtProduct() 需要时间,因此您的包含 self.pageNumber += 1 的代码块甚至在 fetchProduct 的 URLSession 启动之前就已执行。所以,这种方法行不通。

【讨论】:

    【解决方案3】:

    完成块和调度队列是两个不同的概念。

    当你的函数执行动作需要时间运行,并且需要返回并运行一些代码,即使函数已经“结束”时,使用完成块。例如,

    func networkCall(foo: Int, completion:@escaping (_ result:Bool)-> Void))
    func otherFunc(){...}
    func A(){
       networkCall(foo:1){ (success) in
           // handle your stuff
       }
       otherFunc()
    }
    

    当您运行 A() 时,它首先运行 networkCall(),但是 networkCall() 可能需要一些时间来运行网络请求并且应用程序继续运行 otherFunc()。当网络请求完成后,networkCall() 可以调用它的完成块,以便 A() 可以再次处理它。

    Dispatch Queue 是 Apple 安全封装的线程东西。网络请求也可以在主线程中执行,但会阻塞其他功能。

    一种常见的做法是在后台队列中调用网络请求 DispatchQueue.global(qos: .background).async 并在完成后调用完成块。如果需要在 UI 等主线程中更新任何内容,请在 DispatchQueue.main.async 中进行更新

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-05
      相关资源
      最近更新 更多