我有一个基于NSDocument的Cocoa应用程序,可以向用户显示文本文档。文档内容在后台队列中读取,这会导致问题:
我将NSAttributedString
与图像一起使用,即它可以包含NSTextAttachment
和NSTextAttachmentCell
。当我尝试初始化图像的附件并在 Xcode 中激活主线程检查器时,出现以下错误:
// On background queue:
let attachment = NSTextAttachment()
attachment.attachmentCell = NSTextAttachmentCell(imageCell: image) <-
"[NSCell init] must be used from main thread only"
我的第一次尝试是将该代码包装在DispatchQueue.main.sync {}
中,但这会导致在自动保存或用户保存文档时偶尔与NSDocument
死锁。
自动保存会阻塞主队列,我的代码会在后台运行尝试读取文档,但这最终陷入了僵局,因为我无法调用主队列来创建文本附件。
我的问题:
我是否可以忽略 Xcode 中的主线程检查器并在后台队列上实例化NSTextAttachmentCell
?
我在后台队列上所做的只是使用其附件初始化属性字符串。对主队列进行了进一步修改。
事件的顺序
- 线程 2(bg 队列(:由于某种原因需要更新 abc.txt。通过
NSFileCoordinator
获取对 abc .txt的读/写访问权限- 线程 2 (bg( 现在在 NSFileCoordinator 块中
线程 - 1(主线程(:用户启动的 NSDocument 保存,NSDocument 请求对 abc 的写入访问权限.txt通过
NSFileCoordinator
线程- 1(主(现在被阻止,正在等待线程 2 的文件协调器锁定
- 线程 2(bg 队列(:在文件协调器块中移动...,尝试初始化 NSAttributedString,哦,它包含一个附件,无法初始化后台队列上的
NSTextAttachmentCell
,让我把它交给主队列真正的'快速' ...⚡️ 僵局 ⚡️- 线程 2 (bg( 现在正在等待线程 1 (MAIN(,线程 1 (MAIN( 在其文件协调器访问块前面等待线程 2 (bg( 完成其文件协调器块。
不应忽略主线程警告。如果您使用main.async
而不是sync
则添加附件应该永远不会有问题。此参考还有助于定义哪些类在不同线程中表现良好。一般来说,任何 AppKit 视图类型类都应仅在主线程上使用。