I have a Cocoa app based on NSDocument that presents text documents to the user. Document contents are read on a background queue, which causes a problem:
I use NSAttributedString
with images, i.e. it can contain NSTextAttachment
and NSTextAttachmentCell
. When I try to initialize an attachment for an image and I have the main thread checker activated in Xcode, I get the following error:
// On background queue:
let attachment = NSTextAttachment()
attachment.attachmentCell = NSTextAttachmentCell(imageCell: image) <-
"[NSCell init] must be used from main thread only"
My first attempt was to wrap that code in DispatchQueue.main.sync {}
, but this caused a deadlock with NSDocument
once in a while when autosaving took place or when the user saved the document.
Autosaving would block the main queue, my code would run in the background trying to read the document, but this ended up in a deadlock, because I could not call out to the main queue to create the text attachment.
My question:
Is it possible for me to ignore the main thread checker in Xcode and instantiate NSTextAttachmentCell
on a background queue anyway?
All I'm doing on the background queue is initializing the attributed string with its attachments. Further modifications are made on the main queue.
Sequence of events
- Thread 2 (bg queue): Need to update abc.txt for some reason. Get read/write access to abc.txt via
NSFileCoordinator
- Thread 2 (bg) now in NSFileCoordinator block
- Thread 1 (MAIN): User-initiated NSDocument save, NSDocument requests write access to abc.txt via
NSFileCoordinator
- Thread 1 (MAIN) now blocked, waiting for file coordinator lock of Thread 2
- Thread 2 (bg queue): Moving along in file coordinator block..., trying to initialize NSAttributedString, oh, it contains an attachment, can't initialize
NSTextAttachmentCell
on background queue, let me hand this off to the MAIN queue real' quick... ⚡️ DEADLOCK ⚡️- Thread 2 (bg) is now waiting for Thread 1 (MAIN), which is waiting in front of its file coordinator access block for Thread 2 (bg) to finish with its file coordinator block.