在将 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_
一样快。
有没有人以前看到过这个并且知道这是正常的还是我滥用了什么?
答案 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
无论如何都会在运行循环中传递它的事件。