NSManagedObjectContext: performBlockAndWait vs performBlock 通知中心
Posted
技术标签:
【中文标题】NSManagedObjectContext: performBlockAndWait vs performBlock 通知中心【英文标题】:NSManagedObjectContext: performBlockAndWait vs performBlock with notification center 【发布时间】:2014-01-15 19:25:28 【问题描述】:我在使用 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 调用其委托的那一刻,这需要相当长的时间(大约 5 秒)。当我使用 performBlockAndWait:
时,我没有观察到任何可见的延迟 - 调用代理的速度就像我在 _dispatch_async_
中调用 saveToCoreData:
一样快。
有没有人看到过,知道这是正常的还是我滥用了什么?
【问题讨论】:
请注意,通知是在后台线程上引发的,并且您在后台线程中合并对主上下文的更改,这是非常不建议的。使用[self.managedObjectContext performBlockAndWait:...]
这确实很有意义。谢谢丹!我没有意识到connectionWithRequest:
在“(...)为关联的 NSURLConnection 对象启动异步加载操作的线程上调用了委托方法。”这可以通过检查[NSThread isMainThread]
轻松观察到。
【参考方案1】:
正如 Dan 在其中一个 cmets 中所指出的,合并操作应该发生在主线程上。这可以通过更改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:
从线程的角度执行不同。苹果文档说:
performBlock:
和performBlockAndWait:
确保块操作在为上下文指定的队列上执行。performBlock:
方法立即返回,上下文在其自己的线程上执行块方法。使用performBlockAndWait:
方法,上下文仍然在其自己的线程上执行块方法,但该方法在块执行之前不会返回。
这表明,如果问题中描述的问题的真正根本原因是合并发生在后台线程中,那么无论我调用哪种方法,我都应该观察到相同的行为:performBlock:
和 performBlockAndWait:
- 两者都在一个单独的线程中执行。
附带说明,由于NSURLConnection
在启动异步加载操作的同一线程(在我的情况下为主线程)调用委托方法,因此似乎根本不需要使用后台上下文。 NSURLConnections
无论如何都会在运行循环中传递其事件。
【讨论】:
我认为苹果文档的最后一句话是不正确的。 我相信最后一句话是正确的。我最近在使用-com.apple.CoreData.ConcurrencyDebug 1
进行测试时遇到了这种微妙的区别。这个答案很好地解释了它:***.com/a/19439817/140799以上是关于NSManagedObjectContext: performBlockAndWait vs performBlock 通知中心的主要内容,如果未能解决你的问题,请参考以下文章
NSManagedObjectContext:撤消保存操作?
CoreData 多 NSManagedObjectContext 保存通知说明
NSManagedObjectContext: performBlockAndWait vs performBlock 通知中心