Почему для обновления пользовательского интерфейса от фонового потока требуется такое долгое время?

Я понимаю, что все обновления пользовательского интерфейса должны выполняться из основного потока.

Но только ради более глубокого понимания того, как GCD и основная работа рассылки:

У меня есть кнопка, которая запускает сетевой вызов и в своем завершенииHandler я в конце концов делаю:

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 секунд. Очевидно, что если запустить указанный код из основного потока, он сразу изменит цвет границы.

Question1, хотя у меня нет другого кода для запуска, почему переменные пользовательского интерфейса не происходят сразу из фонового потока? Что ждет?

Интересно, что если я нажму кнопку, чтобы сделать сетевой вызов, а затем коснитесь на самом текстовом поле (до 6-7 секунд), цвет границы изменится сразу.

Это происходит из-за:

Из фонового потока я обновил модель, то есть изменил цвет textField, который приостанавливает обновление пользовательского интерфейса/представления... но поскольку мы находимся в фоновом режиме, этот обновленный пользовательский интерфейс может занять несколько секунд

Но потом я сразу нажал на textField и заставил супер быстрый просмотр textField и всех его свойств, которые включают границу от основного потока (фактические касания пользователя всегда обрабатываются через основной поток)... которые хотя еще не красные на экране, но поскольку он краснеет на модели, он будет считывать из него и сразу меняет цвет на красный.

Question2: Правильно ли это наблюдение?

Если я не касаюсь и просто жду:

введите описание изображения здесь

Если я нажимаю:

введите описание изображения здесь


Мой полный код выглядит следующим образом:

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

Если вы попытаетесь сделать обновления пользовательского интерфейса из фонового потока, "результаты будут undefined". Самый распространенный эффект, который я видел, - это то, что вы описываете, - очень длинные задержки до появления обновления. Второй самый распространенный эффект, который я видел, - это крах. Третий наиболее распространенный эффект - это своего рода артефакт рисования.

Результаты выполнения обновлений пользовательского интерфейса из фонового потока действительно недетерминированы. У вас есть несколько процессорных ядер, которые одновременно получают доступ к одним и тем же аппаратным ресурсам, а также точное время между этими доступами непознаваемыми и бесступенчатыми. Это будет похоже на наличие компьютера без дисплея, но 2 клавиатуры и 2 мыши, а 2 оператора одновременно редактируют один и тот же документ. Каждое действие с клавиатурой изменило бы состояние документа и испортило изменения, которые другой человек пытался применить. Курсор будет не в том месте. Объем текста в документе будет отличаться от ожидаемого. Положение прокрутки будет выключено. и т.д. и т.д.

Аналогично, если каждый из двух ядер пытается получить доступ к аппаратным ресурсам для обновления экрана, эти обращения будут пересекаться и конфликты друг с другом.

Как говорит Мартин в своем комментарии, код UIKit является собственностью, поэтому мы не можем знать детали , что идет не так. Все, что мы знаем, это то, что происходят плохие вещи, поэтому НЕ ДЕЛАЙТЕ ЭТО.