У меня есть приложение Cocoa на основе NSDocument, которое представляет текстовые документы пользователю. Содержимое документа считывается в фоновой очереди, что вызывает проблему:
Я использую 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
в фоновой очереди?
Все, что я делаю в фоновой очереди, — это инициализирую атрибутивную строку с ее вложениями. Дальнейшие модификации производятся в основной очереди.
Последовательность событий
- 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.