我有一个用于警报提示的扩展程序。当showAlert()
不是从主线程调用时,我得到“不是从主线程更改UI”的行为。有时根本没有发出警报,有时延迟很大。没有崩溃。但是,如果我将所有函数的代码放在DispatchQueue.main.async
闭包中-一切都很好。为什么?我不应该只从主线程中调用实际上更改UI的代码吗?
@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)
}
}
}
答案 0 :(得分:3)
简短的回答:UIKit
不是线程安全的。您应该从主线程对UIKit
进行 ALL 调用,包括在UIKit
对象上创建或设置属性。很少有 例外,但只有少数例外,并且通常都有据可查。与主线程中的UIKit
对象进行交互永远不会出错,而与后台线程中的UIKit
对象进行交互几乎总是不好。 (其结果是“不确定的”,有时甚至是致命的。)
如果您与主线程中的UIKit
对象进行 ALL 交互,则不会有任何并发问题。在后台执行耗时的联网和/或数字运算代码,然后包装该代码以在调用主线程时将结果呈现给用户。
答案 1 :(得分:2)
您是正确的,所有UI更改操作都应仅在主线程上完成。 如果不是这样,即使您要用您的代码更新UI,系统也不会保证您何时,以什么顺序进行。
现在,您想让主线程尽可能地混乱,而只放置与UI相关的代码是正确的。但是,如果您仔细看一下这些行,您会注意到这些:
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = vc
也在修改UI,但是在主闭包之外。 我相信,如果您在主线程上移动这两行,您的警告将消失!
答案 2 :(得分:0)
可能是您从后台调用show alert方法 因此,您正在使用此代码调用主线程
DispatchQueue.main.async {
window.makeKeyAndVisible()
vc.present(alertController, animated: true)
}
所有UI更改都应在主线程中。