2012-09-19 49 views
4

我正在写一个执行一些CoreData的东西的函数。我想在所有的CoreData操作都执行后,函数只返回。该CoreData的东西涉及到后台前后创建一个对象,然后做一些更多的东西在父上下文:performBlockAndWait创建死锁

+ (void) myFunction 
    NSManagedObjectContext *backgroundContext = [DatabaseDelegate sharedDelegate].backgroundContext; 

    [backgroundContext performBlockAndWait:^{ 
     MyObject *bla = create_my_object_in:backgroundContext; 

     [backgroundContext obtainPermanentIDsForObjects:[[backgroundContext insertedObjects] allObjects] error:nil]; 
     [backgroundContext save:nil]; 

     [[DatabaseDelegate sharedDelegate].parent.managedObjectContext performBlockAndWait:^{ 
     [[DatabaseDelegate sharedDelegate].parent updateChangeCount:UIDocumentChangeDone]; 

     // Do some more stuff 
     NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
     [queue addOperation:someOperation]; 
     }]; 
    }]; 
    return; 
} 

我想返回[queue addOperation:someOperation]后才会发生。 这似乎工作大部分的情况下,但我有一个案件,当这个功能从未返回。它似乎陷入了僵局,我怀疑这是因为performBlockAndWait

我的问题是:

(1)有人能解释为什么这发生死锁?

and

(2)实现相同功能的正确方法是什么?要求是myFunction只有在两个块都被执行后才会返回。

谢谢!

回答

10

让我们假设你从主线程调用myFunction。假设[DatabaseDelegate sharedDelegate].parent.managedObjectContext安排在主线程上。

With [backgroundContext performBlockAndWait:]您正在调度上下文专用背景队列中的块。阻塞主线程。

[.parent.managedObjectContext performBlockAndWait:],您正在主线程上调度一个块,阻止私有队列。

但主线程已被阻塞。所以该块将永远不会执行。并且performBlockAndWait:将永远不会返回。

死锁。

使用异步调度块和完成块。

+0

但我的要求是两个块已经执行后myFunction的只返回。实质上,它们依赖于正在创建的CoreData对象。所以如果我使用performBlock(异步),那么我没有这个保证。 – user1013725

+1

如果parent.managedObjectContext使用主线程并从主线程调用myFunction,则可以将parent.managedObjectContext中的内容从performBlockAndWait中取出,并在myFunction正文中执行它,紧接在第一个performBlockAndWait之后。 –

+0

啊,是的,我认为这是有效的,谢谢! – user1013725

2

您不必等待。你的后台工作执行,然后,完成之前,它在主线程上开始工作,在它完成之前,它执行“someOperation”。你可以用异步替换它,它仍然可以工作。

看这段代码,没有理由使用阻塞版本...

+ (void) myFunction { 
    NSManagedObjectContext *backgroundContext = [DatabaseDelegate sharedDelegate].backgroundContext; 

    [backgroundContext performBlock:^{ 
     // Asynchronous... but every command in this block will run before this 
     // block returns... 
     MyObject *bla = create_my_object_in:backgroundContext; 

     [backgroundContext obtainPermanentIDsForObjects:[[backgroundContext insertedObjects] allObjects] error:nil]; 
     [backgroundContext save:nil]; 

     [[DatabaseDelegate sharedDelegate].parent.managedObjectContext performBlock:^{ 
     // Asynchronous, but this whole block will execute... 
     [[DatabaseDelegate sharedDelegate].parent updateChangeCount:UIDocumentChangeDone]; 

     // Do some more stuff 
     // This will not run until after the stuff above in this block runs... 
     NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
     [queue addOperation:someOperation]; 
     }]; 
     // You will reach here BEFORE the code in the previous block executes, but 
     // the "someOperation" is in that block, so it will not run until that 
     // block is done. 
    }]; 
    // Likewise, you will reach here before the above work is done, but everything 
    // will still happen in the right order relative to each other. 
    return; 
} 
+0

我想你可能误解了我想要的。你最后的评论说:“在上述工作完成之前你会到达这里,但是一切仍然会按照正确的顺序发生。”我想要的是在[queue addOperation:someOperation]执行之前未达到该返回语句。这是不能保证的,是吗? – user1013725

+0

为什么你需要等待呢?它将按照正确的顺序执行其他任何事情。你没有返回任何信息。没有理由等待它执行。执行后必须发生的任何事情都可以在代码中完成。请注意,您仍然可以根据您的实际目标使用异步回调,信号量,通知,委托,NSOperation依赖关系......大量工具。从这些代码中...我看不出有任何理由在你做其他工作之前等待这个请求完成。 –

+0

是的,抱歉,我应该说,由于在myFunction之后执行的代码,对我来说这很重要。特别是,在调用myFunction后立即执行的代码需要保证新创建的MyObject存在于父级managedObjectContext中。 – user1013725