在 Swift 中,如果 Thread.current.isMainThread == false,那么 DispatchQueue.main.sync 递归一次是否安全?
我问的原因是,在我公司的应用程序中,我们发生了崩溃,结果是由于从主线程外调用了某些 UI 方法,例如:
public extension UIViewController {
func presentModally(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
// some code that sets presentation style then:
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
由于这是从许多地方调用的,其中一些有时会从后台线程调用它,因此我们会时不时地崩溃。
由于应用程序超过一百万行代码,修复所有调用站点是不可行的,所以我对此的解决方案只是检查我们是否在主线程上,如果不是,则将调用重定向到主线程,像这样:
public extension UIViewController {
func presentModally(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
guard Thread.current.isMainThread else {
DispatchQueue.main.sync {
presentModally(viewControllerToPresent, animated: flag, completion: completion)
}
return
}
// some code that sets presentation style then:
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
这种方法的好处似乎是:
Dispatch.main.async { ... }
使用了,整个函数体现在必须缩进一个更深的层次,在你的 PR 中产生空白差异,这可能导致恼人的合并冲突并使其成为审阅者更难区分 GitHub 的 PR 差异视图中的显着元素)。同时,替代方案 ,DispatchQueue.main.async
似乎有以下缺点:
self
可能在它运行之前已经释放。这意味着我们必须显式地保留 self (或弱化它)以避免编译器警告。这也意味着,在这个例子中,present(...)
在函数返回给调用者之前不会被调用。这可能会导致模式在调用站点之后的一些其他代码之后弹出,从而导致意外行为。self
。这并不是一个真正的缺点,但它在风格上并不像能够隐含地保留自我那样干净。审查 PR 的我的同事似乎觉得使用“DispatchQueue.main.sync”本质上是不好的和有风险的,并且可能导致僵局。虽然我意识到从主线程使用它确实会死锁,但在这里我们明确地避免在这里使用保护语句来确保我们不是首先在主线程上。
尽管提出了上述所有基本原理,并且尽管无法向我解释死锁实际上是如何发生的,因为只有在函数开始从主线程中调用时才会发生分派,但我的同事们仍然对此持保留态度模式,感觉它可能会导致死锁或以意想不到的方式阻塞 UI。
这种模式绝对不是“完全”安全的。人们可以很容易地制造出僵局:
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
self.presentModally(controller, animated: true)
}
group.wait()
检查isMainThread
的false
不足,严格来说,知道它是否是安全的同步分派到主线程。
但这不是真正的问题。您显然在某处有一些例程,认为它在主线程上运行,但实际上不是。就我个人而言,我会担心在这种误解下运行时该代码还做了什么(例如,不同步的模型更新等)。
您的解决方法,而不是修复问题的根本原因,只是隐藏它。作为一般规则,我不建议围绕代码库中其他地方引入的错误进行编码。您真的应该弄清楚从后台线程调用此例程的位置并解决它。
在如何找到问题方面,希望与崩溃相关的堆栈跟踪会告诉您。我还建议通过单击方案设置中它旁边的小箭头为主线程检查器添加一个断点:
然后运行应用程序,如果遇到此问题,它将在违规行暂停执行,这对于跟踪这些问题非常有用。这通常比从堆栈跟踪进行逆向工程要容易得多。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句