我正在使用MagicalRecord 2.0.3,我无法弄清楚如何在后台保存数据。
根据文档,这样的事情应该有效:
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) {
// Do this hundreds of times
[MyObject createInContext:localContext];
}];
但是,没有任何内容保存到数据库中。我见过很多人发布类似这样的解决方案:
[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) {
// Do this hundreds of times
[MyObject createInContext:localContext];
} completion:^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[[NSManagedObjectContext defaultContext] saveNestedContexts];
}];
}];
这确实将我的数据保存在数据库中,但是由于保存发生在主线程上,我的应用程序暂时没有响应(使用我的数据集,大约3秒,这太长了)。
我也试过这个,但它在保存时也会阻塞:
self.queue = [[NSOperationQueue alloc] init];
[self.queue addOperationWithBlock:^{
NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];
// Do this hundreds of times
[MyObject createInContext:localContext];
[localContext saveNestedContexts];
}];
最后,与此代码相同的阻止效果:
dispatch_queue_t syncQueue = dispatch_queue_create("Sync queue", NULL);
dispatch_async(syncQueue, ^{
NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];
// Do this hundreds of times
[MyObject createInContext:localContext];
[[NSManagedObjectContext contextForCurrentThread] saveNestedContexts];
});
那么,解决这个问题的最佳方法是什么?我需要在后台创建数百个对象,应用程序需要保持响应。
答案 0 :(得分:6)
MagicalRecord在后台工作时使用子上下文。这适用于小的更改,但在导入大量数据时会产生过多的主线程阻塞。
执行此操作的方法是使用并行NSManagedObjectContext并使用NSManagedObjectContextDidSaveNotification
通知和mergeChangesFromContextDidSaveNotification
方法自行合并。请参阅此处的效果测试:http://floriankugler.com/blog/2013/5/11/backstage-with-nested-managed-object-contexts
保存嵌套上下文时,必须将所有内容复制到父上下文。与此相反,未被提取的对象(在您合并的上下文中)将不会被mergeChangesFromContextDidSaveNotification
合并。这就是它更快的原因。
如果要在批量保存并使用NSFetchResultsController后立即显示这些结果,则可能会遇到问题。有关解决方案,请参阅以下问题: NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext
要获得更多性能提示,请查看以下问题:Implementing Fast and Efficient Core Data Import on iOS 5
创建自己的上下文。
NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[importContext setPersistentStoreCoordinator:yourPersistentStoreCoordinator];
[importContext setUndoManager:nil]; // For importing you don't need undo: Faster
// do your importing with the new importContext
// …
NSError* error = nil;
if(importContext.hasChanges) {
if(![importContext save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
}
确保您正在侦听对托管对象上下文的保存。
[[NSNotificationCenter defaultCenter]
addObserver:singleton
selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification object:nil];
在contextDidSave中:您自己合并更改。
- (void) contextDidSave:(NSNotification*) notification
{
if(![notification.object isEqual:self.mainContext]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.mainContext mergeChangesFromContextDidSaveNotification:notification];
});
}
}
答案 1 :(得分:1)
托管对象上下文不是线程安全的,因此如果您需要使用Coredata对象进行任何后台工作(即长时间运行的导入/导出功能而不阻塞主UI),您将需要在后台执行此操作线程。
在这些情况下,您需要在后台线程上创建一个新的托管对象上下文,遍历您的coredata操作,然后通知您的更改的主要上下文。
你可以在这里找到一个如何运作的例子