"DispatchQueue.main.async"和“已完成"是什么:@escaping()-> ()'在此代码段中是什么意思? [英] What does 'DispatchQueue.main.async' and 'completed: @escaping () -> ()' mean in this snippet of code?

查看:111
本文介绍了"DispatchQueue.main.async"和“已完成"是什么:@escaping()-> ()'在此代码段中是什么意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本上,这是一个简单的项目,其中涉及一个表视图,该表视图根据从api的JSON解析的数据进行更新.我相信DispatchQueue.main.asynccompleted: @escaping () -> ()与更新/重新加载表视图有关,但是我不确定它是如何工作的.请解释一下这两者的作用.

Basically this is a simple project that involves a tableview that is updated according to data that is parsed from JSON from an api. I believe DispatchQueue.main.async and completed: @escaping () -> () have something to do with updating/reloading the tableview but I'm not sure how it works. Explanation would be appreciated on what these two do.

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()        
    }
}

推荐答案

  1. DispatchQueue.main.async { ... }的意思是在主队列的花括号内运行以下代码"(必须在其中运行所有UI更新). URLSession闭包和委托方法在专用的URLSession队列上运行,但是UI(和模型)更新应在主队列上进行.

  1. The DispatchQueue.main.async { ... } means "run this following code inside the braces on the main queue" (which is where all UI updates must run). URLSession closures and delegate methods run on a dedicated URLSession queue, but UI (and model) updates should happen on the main queue.

FYI,将代码分派到另一个队列的两种常见方法是asyncsync.它们非常相似,但是async异步运行(即async调用之后的代码将不等待主线程完成调用completed之前继续执行),而sync同步运行(即它将等待).在这种情况下,让URLSession队列等待主队列完成是没有意义的,因此async是合适的.

FYI, the two common methods of dispatching code to another queue are async and sync. They're very similar, but async runs asynchronously (i.e. the code after the async call won't wait for the main thread to finish calling completed before continuing), and sync runs synchronously (i.e. it would wait). In this case, there's no point in having the URLSession queue to wait for the main queue to finish, so async is appropriate.

completed: @escaping () -> ()表示:

  • there is a parameter to fetchData, called completed;
  • that this parameter is a "closure" (i.e. an anonymous bit of code that the caller can provide; see The Swift Programming Language: Closures for more information);
  • this closure that takes no parameters, itself, nor returns anything; and
  • this closure "escapes" (meaning that it won't necessarily be run by the time the fetchData method returns).
     

因此,您可以像这样传递一个要调用的代码块(当您在dataTask闭包中看到completed()时):

So, you could pass a block of code to be called (when you see completed() in the dataTask closure) like so:

fetchData(completed: {
    print("Success")
    self.tableView.reloadData()  // make sure to reload table when request is done
})

但是,您的示例使用了跟踪闭包"语法,其中最终闭包(在这种情况下为唯一的闭包)可以作为参数省略,而只是在fetchData调用之后添加,导致相同的行为(但语法更简洁):

But, your example uses "trailing closure" syntax, where the final closure (and in this case, the only closure) can be omitted as a parameter, and just added after the fetchData call, resulting in the same behavior (but more concise syntax):

fetchData {
    print("Success")
    self.tableView.reloadData()
}

或者,甚至更好:

fetchData { [weak self] in
    print("Success")
    self?.tableView.reloadData()
}

  • 在一个不相关的观察中,您永远不会更新您的heroes属性.至少,您应该这样做:

  • In an unrelated observation, you never update your heroes property. At the very least, you should do:

    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()
    

    请注意,您要更新self.heroes属性 async闭包,以确保您没有从后台线程更新该属性.数组不是线程安全的,并且通过在主线程上更新该属性,可以避免出现任何竞争状况.

    Note, you want to update the self.heroes property inside the async closure to make sure that you don’t update the property from a background thread. Arrays are not thread-safe, and by updating that property on the main thread, you avoid any race conditions.

    我还建议其他许多改进(在dataTask中使用对selfweak引用;向completed闭包中添加一个参数,以便调用者知道它是否成功并显示警告,如果不是). t等),但以上是我建议的最低要求.

    There are many other improvements I could suggest (use weak reference to self in dataTask; add a parameter to your completed closure so the caller knows if it was successful and display warning if it wasn’t, etc.), but the above is the bare minimum that I’d suggest.

    这篇关于"DispatchQueue.main.async"和“已完成"是什么:@escaping()-> ()'在此代码段中是什么意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆