6

我正在编写一个执行一些 CoreData 东西的函数。我希望该函数仅在所有 CoreData 操作都执行后才返回。CoreData 的东西包括在后台上下文中创建一个对象,然后在父上下文中做更多的事情:

+ (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)有人可以解释为什么会发生这种死锁吗?

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

谢谢!

4

2 回答 2

13

假设您正在myFunction从主线程调用。让我们想象一下[DatabaseDelegate sharedDelegate].parent.managedObjectContext被安排在主线程上。

[backgroundContext performBlockAndWait:]您一起在上下文私有后台队列上安排一个块。阻塞主线程。

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

但是主线程已经被阻塞了。所以这个块永远不会执行。并且performBlockAndWait:永远不会回来。

僵局。

使用带有完成块的异步调度块。

于 2012-09-19T18:37:44.537 回答
2

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

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

+ (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;
}
于 2012-09-19T19:42:12.770 回答