Jas*_*ark 0 closures grand-central-dispatch ios swift
基本上,这是一个简单的项目,涉及一个表视图,该表视图根据从api的JSON解析的数据进行更新。我相信DispatchQueue.main.async并completed: @escaping () -> ()有事情做与更新/重装tableview中,但我不知道它是如何工作的。对于这两个功能的解释将不胜感激。
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var heroes = [HeroStats]()
override func viewDidLoad() {
super.viewDidLoad()
fetchData {
print("Success")
}
}
func fetchData(completed: @escaping () -> ()) {
let jsonUrlString = "https://api.opendota.com/api/heroStats"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
if error == nil {
do {
let heroes = try JSONDecoder().decode([HeroStats].self, from: data)
DispatchQueue.main.async {
completed()
}
} catch let error {
print(error)
}
}
}.resume()
}
}
Run Code Online (Sandbox Code Playgroud)
的DispatchQueue.main.async { ... }意思是“运行在主队列中的括号内这下面的代码”(其是所有UI更新必须运行)。URLSession闭包和委托方法在专用URLSession队列上运行,但UI(和模型)更新应在主队列上进行。
仅供参考,将代码分配到另一个队列的两种常见方法是async和sync。它们非常相似,但是async异步运行(即,async调用后的代码将不等待主线程完成调用completed再继续),而sync同步运行(即,它将等待)。在这种情况下,让URLSession队列等待主队列完成async是没有意义的,因此是适当的。
在completed: @escaping () -> ()表明:
fetchData称为completed;fetchData方法返回时运行)。因此,您可以像下面这样传递代码块(completed()在dataTask闭包中看到时):
fetchData(completed: {
print("Success")
self.tableView.reloadData() // make sure to reload table when request is done
})
Run Code Online (Sandbox Code Playgroud)
但是,您的示例使用了“跟踪闭包”语法,其中可以将最终闭包(在这种情况下,唯一的闭包)作为参数省略,并在fetchData调用之后添加,导致相同的行为(但语法更简洁) :
fetchData {
print("Success")
self.tableView.reloadData()
}
Run Code Online (Sandbox Code Playgroud)
或者,甚至更好:
fetchData { [weak self] in
print("Success")
self?.tableView.reloadData()
}
Run Code Online (Sandbox Code Playgroud)在不相关的观察中,您永远不会更新heroes属性。至少,您应该这样做:
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print(error ?? "Unknown error")
return
}
do {
let heroes = try JSONDecoder().decode([HeroStats].self, from: data)
DispatchQueue.main.async {
self.heroes = heroes
completed()
}
} catch let error {
print(error)
}
}.resume()
Run Code Online (Sandbox Code Playgroud)
请注意,您要更新的self.heroes属性里面的async封闭,以确保您不更新从后台线程属性。数组不是线程安全的,并且通过在主线程上更新该属性,可以避免任何竞争情况。
我可以建议其他许多改进(使用in的weak引用;向您的闭包中添加一个参数,以便调用者知道它是否成功,如果失败则显示警告,等等),但是以上是我的最低要求建议。selfdataTaskcompleted