问题描述
在我的Cocoa应用程序中,我在后台进行了一些计算。后台工作由DispatchQueue.global(qos: .utility).async
运行。
此后台任务可能通过通过NSAlert
显示模式DispatchQueue.main.async
来报告错误。
此外,在我的应用程序中,用户可以运行NSOpenPanel
打开某些文件(使用NSOpenPanel.runModal
)。
问题在于,如果用户打开NSOpenPanel
,同时后台任务显示NSAlert
,则应用程序可能会挂起。
- 用户打开模式
NSOpenPanel
- 后台任务在
NSAlert
上方打开模式NSOpenPanel
- 用户点击
NSOpenPanel
内的 Close (尽管存在更多模式NSOpenPanel
,它实际上仍可以访问NSAlert
)。 -
NSAlert
和NSOpenPanel
都被关闭,应用程序挂起,并且主线程在NSOpenPanel.runModal()
内部被阻塞
如果用户先关闭 - 应用程序将不会挂起。
NSAlert
然后关闭NSOpenPanel
,最小代码示例(将测试IBaction绑定为主窗口中按钮的操作)
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject,NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!
@IBAction func test(_ sender: Any) {
//run some work in background
DispatchQueue.global(qos: .utility).async
{
sleep(1) //some work
//report errors in the main thread.
DispatchQueue.main.async {
let alert = NSAlert();
alert.informativeText = "Close NSOpen panel before this alert to reproduct the hang."
alert.runModal()
}
}
//user want to open a file and opens the open file dialog
let dlg = NSOpenPanel();
dlg.runModal();
}
}
那么,这段代码有什么问题,为什么在特定的用例中它会导致挂起?以及如何防止这种死机?
补充说明:我发现,然后用dlg.runModal()
替换NSApp.RunModal(for: dlg)
(与Apple文档完全相同),这将解决上述用例中的问题。
但是它仍然会在关闭NSAlert
之后立即自动关闭NSOpenPanel
。而且我仍然不明白为什么这样做会如此。
更新
我更新了上面的代码,以包含最小可复制应用程序的AppDelegate类的完整代码。要重现此案例,只需在XCode中创建新的SwiftApp,替换AppDelegate代码,在主窗口中添加按钮,并使用test
func来绑定按钮的动作。我还在github上放置了完整的可编译项目:https://github.com/snechaev/hangTest
排除了用于配置NSOpenPanel和NSAlert及其结果处理的代码,因为此类代码不会影响挂起。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)