iOS Что мне следует вызвать из основного потока при смене интерфейса? - PullRequest
0 голосов
/ 18 января 2019

У меня есть расширение для оповещения. Когда showAlert() вызывается не из основного потока, я получаю поведение "изменение интерфейса не из основного потока". Иногда оповещение не представляется вообще, иногда с большой задержкой. Не вылетает. Но если я помещу весь код функции в закрытие DispatchQueue.main.async - все в порядке. Зачем? Разве я не должен вызывать из основного потока только код, который фактически изменяет интерфейс?

@objc extension UIAlertController {

static func showAlert(title: String?, message: String? = nil, closeActionTitle: String? = "OK", preferredStyle: UIAlertControllerStyle = .alert, actions: [UIAlertAction]? = nil) {
        let alertController = UIAlertController(title: title,
                                                message: message,
                                                preferredStyle: preferredStyle)
        var allActions = [UIAlertAction]()
        if let closeTitle = closeActionTitle {
            allActions.append(UIAlertAction(title: closeTitle, style: .cancel))
        }
        allActions.append(contentsOf: actions ?? [])
        allActions.forEach { alertController.addAction($0) }

        let vc = ClearViewController()
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = vc
        window.backgroundColor = AppTheme.color.clear
        window.windowLevel = UIWindowLevelAlert

    DispatchQueue.main.async {
        window.makeKeyAndVisible()
        vc.present(alertController, animated: true)
    }
}

}

Ответы [ 3 ]

0 голосов
/ 18 января 2019

Вы правы, все операции по смене интерфейса должны выполняться только в основном потоке. Если нет, система не гарантирует, когда, в каком порядке, даже если пользовательский интерфейс будет обновлен с помощью вашего кода.

Теперь вы правы, если хотите как можно меньше загромождать основной поток и добавлять только код, связанный с пользовательским интерфейсом. Но если вы посмотрите ближе на линии, вы заметите следующее:

let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = vc

, которые также изменяют пользовательский интерфейс, но находятся за пределами основного закрытия. Я считаю, что если вы переместите эти 2 строки в главном потоке, ваше предупреждение исчезнет!

0 голосов
/ 18 января 2019

Краткий ответ: UIKit не является потокобезопасным. Вы должны ALL вызывать UIKit, включая создание или настройку свойств для UIKit объектов из основного потока. Есть несколько незначительных исключений из этого, но только несколько, и они обычно хорошо документированы. Взаимодействие с UIKit объектами из основного потока никогда не бывает плохим, а взаимодействие с UIKit объектами из фоновых потоков - почти всегда плохо. (Результаты этого «неопределенные» и иногда фатальные.)

Если вы выполните ALL взаимодействие с UIKit объектами из основного потока, у вас не возникнет проблем с параллелизмом. Выполните трудоемкий сетевой и / или кодовый перебор номера в фоновом режиме, а затем оберните код, чтобы представить результаты пользователю при вызовах основного потока.

0 голосов
/ 18 января 2019

Может быть, вы вызываете метод show alert из фона Итак, вы вызываете основной поток с этим кодом

DispatchQueue.main.async {
    window.makeKeyAndVisible()
    vc.present(alertController, animated: true)
}

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

...