我已经在完成块之后实现了,完成了一个块,然后相应地更新了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
}
但是,以下方法清楚地显示了静态调用将在后台线程中发生,一旦完成,将更新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感到困惑。
推荐哪个?
答案 0 :(得分:1)
在第一种方法中,您调用内部使用NSURLSession的方法fetchProducts()
。使用NSURLSession的REST调用在后台运行,并且在REST调用完成时,将调用任务的完成。在该完成中,您调用fetchProducts()
的完成处理程序。在我看来,这种方法很好。
在第二种方法中,您使用全局后台队列并异步调用NSURLSession API(我认为是),而不必等待调用完成。主队列上的代码将立即被调用,此时NSURLSession任务可能 已经完成。 因此,这种方法是有问题的。
答案 1 :(得分:1)
完成块和调度队列是两个不同的概念。
当您的函数执行动作需要一些时间才能运行并且需要返回并运行一些代码(即使函数已“终止”)时,使用完成块。例如,
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
答案 2 :(得分:1)
只要异步获取fetchProducts,第一个方法就可以。在fetchProducts()中,如果您在主队列中调用完成块,则甚至不需要在doPaging()方法中再次获取主队列。
在第二种方法中,您正在全局(并发)队列中调用fetchProducts()。尽管全局队列按照添加到队列中的顺序启动每个任务,但它们同时运行任务。而且由于fechtProduct()需要时间,因此包含f self.pageNumber += 1
的代码块在fetchProduct的URLSession启动之前就已执行。因此,这种方法行不通。