如何在后台创建多个对象?

时间:2021-11-06 23:10:46

I'm using MagicalRecord 2.0.3 and I can't really figure out how to save data in the background.

我正在使用MagicalRecord 2.0.3,我无法弄清楚如何在后台保存数据。

According to the documentation, something like this should work:

根据文档,这样的事情应该工作:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) {
    // Do this hundreds of times
    [MyObject createInContext:localContext];
}];

However, nothing is saved to the database. I've seen multiple people posting solutions similar to this:

但是,没有任何内容保存到数据库中。我见过多个人发布类似这样的解决方案:

[MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext) {
    // Do this hundreds of times
    [MyObject createInContext:localContext];
} completion:^{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [[NSManagedObjectContext defaultContext] saveNestedContexts];
    }];
}];

This does save my data in the database, however since the save happens on the main thread, my application is unresponsive for a while (with my dataset, about 3 seconds which is way too long).

这确实将我的数据保存在数据库中,但是由于保存发生在主线程上,我的应用程序暂时没有响应(使用我的数据集,大约3秒,这太长了)。

I've also tried this, but it also blocks up while saving:

我也尝试了这个,但它在保存时也会阻塞:

self.queue = [[NSOperationQueue alloc] init];

[self.queue addOperationWithBlock:^{
    NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];

    // Do this hundreds of times
    [MyObject createInContext:localContext];

    [localContext saveNestedContexts];
}];

And lastly, same blocking effect with this code:

最后,与此代码相同的阻止效果:

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

So, what is the best way to solve this? I need to create hundreds of objects in the background and the app needs to remain responsive.

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

2 个解决方案

#1


6  

MagicalRecord uses a child context when doing work in the background. This works fine for small changes, but will create excessive main thread blocking when importing large amounts of data.

MagicalRecord在后台工作时使用子上下文。这适用于小的更改,但在导入大量数据时会产生过多的主线程阻塞。

The way to do it is to use a parallel NSManagedObjectContext and to do the merging yourself with the NSManagedObjectContextDidSaveNotification notification and the mergeChangesFromContextDidSaveNotification method. See performance tests here: http://floriankugler.com/blog/2013/5/11/backstage-with-nested-managed-object-contexts

这样做的方法是使用并行NSManagedObjectContext并使用NSManagedObjectContextDidSaveNotification通知和mergeChangesFromContextDidSaveNotification方法自行合并。请参阅此处的性能测试:http://floriankugler.com/blog/2013/5/11/backstage-with-nested-managed-object-contexts

When saving a nested contexts everything has to be copied to the parent context. As opposed to this, objects that have not been fetched (in the context into which you are merging) will not be merged by mergeChangesFromContextDidSaveNotification. This is what makes it faster.

保存嵌套上下文时,必须将所有内容复制到父上下文。与此相反,mergeChangesFromContextDidSaveNotification不会合并尚未获取的对象(在您合并的上下文中)。这就是它更快的原因。

You might encounter problems if you want to display these results right away after saving in batches and using an NSFetchResultsController. See the following question for a solution: NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext

如果要在批量保存并使用NSFetchResultsController后立即显示这些结果,则可能会遇到问题。有关解决方案,请参阅以下问题:带谓词的NSFetchedResultsController忽略从不同NSManagedObjectContext合并的更改

For more performance tips take a look at this question: Implementing Fast and Efficient Core Data Import on iOS 5

有关更多性能提示,请查看以下问题:在iOS 5上实现快速高效的核心数据导入

Create your own context.

创建自己的上下文。

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

Make sure you are listening for the saves to managed object contexts.

确保您正在侦听对托管对象上下文的保存。

[[NSNotificationCenter defaultCenter] 
              addObserver:singleton 
                 selector:@selector(contextDidSave:)
                     name:NSManagedObjectContextDidSaveNotification object:nil];

In the contextDidSave:you merge the change yourself.

在contextDidSave中:您自己合并更改。

- (void) contextDidSave:(NSNotification*) notification
{
  if(![notification.object isEqual:self.mainContext]) {
    dispatch_async(dispatch_get_main_queue(), ^{
      [self.mainContext mergeChangesFromContextDidSaveNotification:notification];
    });
  }
}

#2


1  

Managed object contexts are not thread safe so if you ever need to do any kind of background work with your Coredata objects (i.e. a long running import/export function without blocking the main UI) you will want to do that on a background thread.

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

In these cases you will need to create a new managed object context on the background thread, iterate through your coredata operation and then notify the main context of your changes.

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

You can find an example of how this could work here

你可以在这里找到一个如何工作的例子

Core Data and threads / Grand Central Dispatch

核心数据和线程/ Grand Central Dispatch

#1


6  

MagicalRecord uses a child context when doing work in the background. This works fine for small changes, but will create excessive main thread blocking when importing large amounts of data.

MagicalRecord在后台工作时使用子上下文。这适用于小的更改,但在导入大量数据时会产生过多的主线程阻塞。

The way to do it is to use a parallel NSManagedObjectContext and to do the merging yourself with the NSManagedObjectContextDidSaveNotification notification and the mergeChangesFromContextDidSaveNotification method. See performance tests here: http://floriankugler.com/blog/2013/5/11/backstage-with-nested-managed-object-contexts

这样做的方法是使用并行NSManagedObjectContext并使用NSManagedObjectContextDidSaveNotification通知和mergeChangesFromContextDidSaveNotification方法自行合并。请参阅此处的性能测试:http://floriankugler.com/blog/2013/5/11/backstage-with-nested-managed-object-contexts

When saving a nested contexts everything has to be copied to the parent context. As opposed to this, objects that have not been fetched (in the context into which you are merging) will not be merged by mergeChangesFromContextDidSaveNotification. This is what makes it faster.

保存嵌套上下文时,必须将所有内容复制到父上下文。与此相反,mergeChangesFromContextDidSaveNotification不会合并尚未获取的对象(在您合并的上下文中)。这就是它更快的原因。

You might encounter problems if you want to display these results right away after saving in batches and using an NSFetchResultsController. See the following question for a solution: NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext

如果要在批量保存并使用NSFetchResultsController后立即显示这些结果,则可能会遇到问题。有关解决方案,请参阅以下问题:带谓词的NSFetchedResultsController忽略从不同NSManagedObjectContext合并的更改

For more performance tips take a look at this question: Implementing Fast and Efficient Core Data Import on iOS 5

有关更多性能提示,请查看以下问题:在iOS 5上实现快速高效的核心数据导入

Create your own context.

创建自己的上下文。

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

Make sure you are listening for the saves to managed object contexts.

确保您正在侦听对托管对象上下文的保存。

[[NSNotificationCenter defaultCenter] 
              addObserver:singleton 
                 selector:@selector(contextDidSave:)
                     name:NSManagedObjectContextDidSaveNotification object:nil];

In the contextDidSave:you merge the change yourself.

在contextDidSave中:您自己合并更改。

- (void) contextDidSave:(NSNotification*) notification
{
  if(![notification.object isEqual:self.mainContext]) {
    dispatch_async(dispatch_get_main_queue(), ^{
      [self.mainContext mergeChangesFromContextDidSaveNotification:notification];
    });
  }
}

#2


1  

Managed object contexts are not thread safe so if you ever need to do any kind of background work with your Coredata objects (i.e. a long running import/export function without blocking the main UI) you will want to do that on a background thread.

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

In these cases you will need to create a new managed object context on the background thread, iterate through your coredata operation and then notify the main context of your changes.

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

You can find an example of how this could work here

你可以在这里找到一个如何工作的例子

Core Data and threads / Grand Central Dispatch

核心数据和线程/ Grand Central Dispatch