NSManagedObjectContext:performBlockAndWait vs performBlock with notification center

时间:2014-01-15 19:25:28

标签: ios core-data nsfetchedresultscontroller nsmanagedobjectcontext nsnotificationcenter

在将 NSManagedObjectContext performBlock:与通知中心一起使用时,我遇到了一些有趣的行为。

从主UI线程我触发异步数据下载(使用 NSURLConnection connectionWithRequest:)。当数据到达时,将调用以下委托方法:

- (void)downloadCompleted:(NSData *)data
{
    NSArray *new_data = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

    self.backgroundObjectContext = [[NSManagedObjectContext alloc]   
                          initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    self.backgroundObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;

    [self.backgroundObjectContext performBlockAndWait:^{
        [self saveToCoreData:new_data];
    }];
}

savetoCoreData:方法只是将新数据保存到后台上下文中:

- (void)saveToCoreData:(NSArray*)questionsArray
{
    for (NSDictionary *questionDictionaryObject in questionsArray) {
        Question *newQuestion = [NSEntityDescription 
                      insertNewObjectForEntityForName:@"Question" 
                               inManagedObjectContext:self.backgroundObjectContext];

        newQuestion.content = [questionDictionaryObject objectForKey:@"content"];            
    }

    NSError *savingError = nil;
    [self.backgroundObjectContext save:&savingError];
}

在视图控制器中,在viewDidLoad中,我将观察者添加到通知中心:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) 
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:nil];

然后在contexChanged:中我将背景上下文与主上下文合并,以便在我的视图可以更新的地方调用我的NSFetchedResultsController的委托方法:

- (void)contextChanged:(NSNotification*)notification
{
    if ([notification object] == self.managedObjectContext) return;

    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}

这一切似乎运作良好,但有一件事困扰着我。在downloadCompleted:方法中,我使用performBlock:代替performBlockAndWait:时,通知似乎会延迟。从后台线程save: NSFetchedResultsController 调用其委托的那一刻起,它需要花费很多时间(大约5s)。当我使用performBlockAndWait:时,我没有观察到任何可见的延迟 - 代理的调用速度就像我在saveToCoreData:内调用_dispatch_async_一样快。

有没有人以前看到过这个并且知道这是正常的还是我滥用了什么?

1 个答案:

答案 0 :(得分:2)

正如Dan在其中一条评论中指出的那样,合并操作应该在主线程上进行。通过更改contextChanged:方法来执行以下操作可以很容易地观察到这一点:

 - (void)contextChanged:(NSNotification*)notification
{
    if ([notification object] == self.managedObjectContext) return;

    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:@selector(contextChanged:) 
                               withObject:notification 
                            waitUntilDone:YES];
        return;
    }

    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}

通过此更改,performBlock:performBlockAndWait:都有效。

只要这在某种程度上解释了为什么问题首先出现,我仍然不明白为什么performBlock:performBlockAndWait:与线程角度的表现不同。 Apple文档说:

  

performBlock:performBlockAndWait:确保在为上下文指定的队列上执行块操作。 performBlock:方法立即返回,上下文在其自己的线程上执行块方法。使用performBlockAndWait:方法,上下文仍在其自己的线程上执行块方法,但该方法在块执行之前不会返回。

这表明,如果问题中描述的问题的真正根本原因是后台线程中发生了合并,那么无论我调用哪种方法,我都应该观察到相同的行为:performBlock:和{ {1}} - 两者都在sperate线程中执行。

作为旁注,由于performBlockAndWait:在启动异步加载操作的同一线程上调用委托方法 - 在我的情况下是主线程 - 使用背景上下文似乎根本不需要。 NSURLConnection无论如何都会在运行循环中传递它的事件。