【问题标题】:How to create multiple objects in background?如何在后台创建多个对象?
【发布时间】:2012-09-16 04:16:41
【问题描述】:

我正在使用 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];
});

那么,解决这个问题的最佳方法是什么?我需要在后台创建数百个对象,并且应用需要保持响应。

【问题讨论】:

  • 新的嵌套上下文已经开始对 MagicalRecord 中的许多保存 API 造成严重破坏。虽然我知道这些问题,并且现在正在讨论一些修复,但我总是愿意接受建议。
  • 也许你最好在没有像 MR 这样的框架的情况下使用 Core Data?
  • 您是否曾使用 MagicalRecord 找到解决方案?我有同样的问题(在后台更新时 UI 锁定),我找不到解决方案。谢谢!
  • 最后我从我的应用程序中删除了 MagicalRecord。我现在使用github.com/jksk/NLCoreData,它的魔力少了很多,而且效果似乎好多了。
  • MagicalRecord 2.1 表现更好吗?后台保存现在真的有效吗?

标签: ios multithreading core-data magicalrecord


【解决方案1】:

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];
    });
  }
}

【讨论】:

  • 这到底是如何工作的?我面临的问题是我的主线程由于要导入大量数据而阻塞。我正在使用 MagicalRecord,但无法实现您在此处编写的内容...
【解决方案2】:

托管对象上下文不是线程安全的,因此如果您需要对 Coredata 对象进行任何类型的后台工作(即长时间运行的导入/导出功能而不阻塞主 UI),您将希望在后台执行此操作线程。

在这些情况下,您需要在后台线程上创建一个新的托管对象上下文,遍历您的 coredata 操作,然后通知主上下文您的更改。

您可以在此处找到如何工作的示例

Core Data and threads / Grand Central Dispatch

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-23
    相关资源
    最近更新 更多