NSManagedObjectContext:performBlockAndWait vs performBlock with notification center

eve*_*ive 5 core-data nsfetchedresultscontroller nsnotificationcenter nsmanagedobjectcontext ios

使用,当我遇到有趣的行为来的NSManagedObjectContextperformBlock:与通知中心.

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

- (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];
    }];
}
Run Code Online (Sandbox Code Playgroud)

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];
}
Run Code Online (Sandbox Code Playgroud)

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

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) 
                                             name:NSManagedObjectContextDidSaveNotification
                                           object:nil];
Run Code Online (Sandbox Code Playgroud)

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

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

    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}
Run Code Online (Sandbox Code Playgroud)

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

有没有人以前看过,知道这是正常还是我滥用了什么?

eve*_*ive 2

正如 Dan 在评论之一中指出的那样,合并操作应该发生在主线程上。contextChanged:通过更改方法来执行以下操作可以轻松观察到这一点:

\n\n
 - (void)contextChanged:(NSNotification*)notification\n{\n    if ([notification object] == self.managedObjectContext) return;\n\n    if (![NSThread isMainThread]) {\n        [self performSelectorOnMainThread:@selector(contextChanged:) \n                               withObject:notification \n                            waitUntilDone:YES];\n        return;\n    }\n\n    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

随着这一变化, 和performBlock:performBlockAndWait:开始发挥作用。

\n\n

只要这在某种程度上解释了问题首先发生的原因,我仍然不明白为什么performBlock:并且performBlockAndWait:从线程的角度执行不同的操作。苹果文档说:

\n\n
\n

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

\n
\n\n

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

\n\n

作为旁注,由于在NSURLConnection启动异步加载操作的同一线程(在我的例子中是主线程)上调用委托方法,因此使用后台上下文似乎根本没有必要。NSURLConnections无论如何,在运行循环中传递其事件。

\n