我最近在玩UIDocument并且在dispatch_sync / dispath_async上混淆概念
我有一个获取所有文档信息的方法
for (int i=0; i < noteDocuments.count; i++) {
NSURL * fileURL = [noteDocuments objectAtIndex:i];
NoteDocument *doc = [[NoteDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
[doc closeWithCompletionHandler:^(BOOL success) {
[self addOrUpdateInfoWithDoc:doc];
}];
}];
}
使用这种简单的方法,一切都很好。
然后我尝试dispatch_sync / dispath_async
首先使用dispath_sync
dispatch_sync(dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{
for (int i=0; i < noteDocuments.count; i++) {
NSURL * fileURL = [noteDocuments objectAtIndex:i];
NoteDocument *doc = [[NoteDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
[doc closeWithCompletionHandler:^(BOOL success) {
[self addOrUpdateInfoWithDoc:doc];
}];
}];
}
});
像这样添加dispatch_sync后,方法永远不会完成。这让我感到困惑,因为在执行块之后dispath_sync应该返回主队列,在这种情况下,openWithCompletionHandler
应该立即返回。
然后好奇地把它改成dispatch_async(仅用于实验)
dispatch_async(dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{
for (int i=0; i < noteDocuments.count; i++) {
NSURL * fileURL = [noteDocuments objectAtIndex:i];
NoteDocument *doc = [[NoteDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
[doc closeWithCompletionHandler:^(BOOL success) {
[self addOrUpdateInfoWithDoc:doc];
}];
}];
}
});
结果也让我感到困惑,我在行[doc openWithCompletionHandler:^(BOOL success) {
任何人都能解释一下引擎盖下的内容吗?这是我理解的,但显然是错误的。
答案 0 :(得分:2)
我相信NoteDocument类派生自UIDocument class。
<击> 所以-openWithCompletionHandler:在主队列上调用completionHandler。
-openWithCompletionHandler:参数:completionHandler
包含要在打开操作结束后执行的代码的块。
在主队列上调用该块。
另外我相信主队列(主线程)上的代码如下。
dispatch_sync(dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT), ^{
...
这意味着您打算等待在主队列中完成队列。是的,它会阻塞主队列,直到队列结束。
openWithCompletionHandler:已完成打开文档,然后将completionHandler块提交给主队列,但主队列已被dispatch_sync阻止。因此,永远不会调用completionHandler块。因此,&#34;测试&#34; queue永远不会完成,dispatch_sync也永远不会完成。
和EXC_BAD_INSTRUCTION,我怀疑-openWithCompletionHandler:可以从另一个线程调用。我不确定,但您可能需要从主线程调用该方法。
击>
顺便说一下,你可能会误解dispatch_queue_create的第二个参数。
dispatch_queue_create("test", DISPATCH_QUEUE_PRIORITY_DEFAULT)
第二个参数是 attr ,它不是队列的优先级。
BSD库函数手册dispatch_queue_create(3)
CREATION
attr参数指定要创建的队列类型,必须是 DISPATCH_QUEUE_SERIAL或DISPATCH_QUEUE_CONCURRENT。
幸运的是,在这种情况下,这不是问题。在&#39;&#39; /usr/include/dispatch/queue.h'&#39;&#39;
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
与
相同#define DISPATCH_QUEUE_SERIAL NULL
修改强>
如果UIDocument -openWithCompletionHandler:是普通方法,它不会导致DEADLOCK。这将是以下顺序。
main queue test queue
runloop
|
|
dispatch_sync [your block] ---->
(*BLOCKED*) [your block]
|
UIDocument -openWithCompletionHandler:
|
(opening document...)
|
(finished to open)
|
<------------------ dispatch_async [the completion block]
|
DONE
(Wake up from dispatch_sync
because test queue was finished)
|
|
the end of the runloop
execute blocks
|
[the completion block]
|
blocks done
|
|
go to the top of the runloop
但是,根据LLDB堆栈跟踪,它似乎与其他方法略有不同。
#0 0x0358dfb6 in semaphore_wait_trap ()
#1 0x032504bf in _dispatch_thread_semaphore_wait ()
#2 0x03249e4b in _dispatch_barrier_sync_f_slow ()
#3 0x0324ee3a in _dispatch_barrier_sync_f ()
#4 0x0324a728 in _dispatch_barrier_sync_slow ()
#5 0x0324a61c in dispatch_barrier_sync ()
#6 0x00a9044b in -[UIDocument(UIDocumentInternal) _performBlock:synchronouslyOnQueue:] ()
#7 0x00a9048d in -[UIDocument(UIDocumentInternal) _performBlockSynchronouslyOnMainThread:] ()
#8 0x00a866a3 in -[UIDocument _registerAsFilePresenterIfNecessary] ()
#9 0x00a87891 in -[UIDocument openWithCompletionHandler:] ()
所以序列如下。
main queue test queue
runloop
|
|
dispatch_sync [your block] ---->
(*BLOCKED*) [your block]
|
UIDocument -openWithCompletionHandler:
|
UIDocument _performBlockSynchronouslyOnMainThread:
|
<------------- dispatch_sync [UIDocument internal block]
(*BLOCKED*)
因此,DEADLOCK。