为什么从后台线程更新 UI 需要这么长时间?

Hon*_*ney 3 grand-central-dispatch ios dispatch-async swift

我知道所有 UI 更新都必须从主线程完成。

但纯粹是为了更深入地了解 GCD 和 dispatch main 是如何工作的:

我有一个运行网络调用的按钮,我最终在它的 completionHandler 中执行以下操作:

self.layer.borderColor = UIColor(red: 255/255.0, green: 59/255.0, blue: 48/255.0, alpha: 1.0).cgColor
self.layer.borderWidth = 3.0
Run Code Online (Sandbox Code Playgroud)

发生颜色变化需要 6-7 秒。显然,如果从主线程运行上面的代码,它会立即改变边框颜色。

问题 1即使我没有任何其他代码可以运行,为什么 UI 更改不会立即从后台线程发生?还在等什么?

有趣的是,如果我点击按钮进行网络调用,然后点击textField 本身(在 6-7 秒之前),边框颜色会立即改变。

发生这种情况是因为:

从后台线程我已经更新了模型,即更改将要更新的 UI/视图排队的 textField 颜色……但是由于我们在后台队列中,因此更新 UI 可能需要几秒钟的时间

但随后我立即点击了 textField 并强制超级快速读取 textField 及其所有属性,包括边框 - 从主线程(实际用户触摸始终通过主线程处理)......即使还没有屏幕上显示为红色,但由于模型上显示为红色,因此它将从中读取并立即将颜色更改为红色。

问题 2:这种观察是否正确?

如果我不点击并等待:

在此处输入图片说明

如果我点击:

在此处输入图片说明


我的完整代码如下:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    @IBAction func isValid(_ sender: Any) {

        let userEmail = textField.text

        let requestURL = NSURL(string: "https://jsonplaceholder.typicode.com")

        var request = URLRequest(url: requestURL as! URL)

        request.httpMethod = "POST"

        let postString = "Anything"

        request.httpBody = postString.data(using: .utf8)

        let task = URLSession.shared.dataTask(with: request) { data, response, error in

            guard let data = data, error == nil else {
                print("error=\(error)")
                return
            }

            if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
            }

            do {

                let json = try? JSONSerialization.jsonObject(with: data, options: [])

                if let _ = json as? [String: Any] {

                    self.textField.layer.borderColor = UIColor(red: 255/255.0, green: 59/255.0, blue: 48/255.0, alpha: 1.0).cgColor
                    self.textField.layer.borderWidth = 3.0

                }

            } catch let error as NSError {
                print(error)
            }

        }
        task.resume()

    }
    override func viewDidLoad() {
        super.viewDidLoad()
    }

}
Run Code Online (Sandbox Code Playgroud)

Dun*_*n C 6

如果您尝试从后台线程进行 UI 更新,“结果是不确定的”。我见过的最常见的效果是你所描述的 - 在更新出现之前延迟很长时间。我见过的第二个最常见的影响是崩溃。第三个最常见的效果是某种绘图神器。

从后台线程执行 UI 更新的结果确实是不确定的。您有多个处理器内核同时访问相同的硬件资源,并且这些访问之间的确切时间是不可知且无限可变的。这就像拥有一台没有显示器但只有 2 个键盘和 2 个鼠标的计算机,并且 2 个操作员同时编辑同一个文档。每个人使用键盘的操作都会改变文档的状态,并搞砸其他人试图应用的更改。光标会在错误的地方。文档中的文本量将与预期不同。滚动位置将关闭。等等等等。

类似地,如果 2 个内核都尝试访问硬件资源以进行屏幕刷新,则这些访问将交叉并相互冲突。

正如 Martin 在他的评论中所说,UIKit 代码是专有的,所以我们无法知道出了什么问题的细节。我们只知道坏事会发生,所以不要那样做。