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

时间:2017-07-05 16:40:35

标签: ios swift grand-central-dispatch dispatch-async

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

但纯粹是为了更深入地了解GCD和派遣主要工作的方式:

我有一个运行网络呼叫的按钮,在我的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

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

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

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

这是因为:

从后台线程我更新了模型,即更改textField颜色,将UI /视图排队等待更新...但由于我们在后台队列中,因此更新的UI可能需要几秒钟才能完成

然后我立即点击textField并强制超快速读取textField及其所有属性,其中包括border-from主线程(实际用户触摸总是通过主线程处理)...即使在屏幕上还没有红色,但由于它在模型上是红色的,它会从中读取并立即将颜色变为红色。

问题2:观察结果是否正确?

如果我没有点击并等待:

enter image description here

如果我点按:

enter image description here

我的完整代码如下:

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

}

1 个答案:

答案 0 :(得分:4)

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

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

同样,如果两个核心都试图访问硬件资源来进行屏幕刷新,那么这些访问将相互交叉并发生冲突。

正如Martin在评论中所说,UIKit代码是专有的,因此我们无法了解 出错的细节。我们所知道的是坏事发生了,所以不要这样做