ios核心数据导入大量数据

时间:2014-03-06 23:24:41

标签: ios core-data

我的应用程序需要从我的Web API导入对象(通常超过5000个对象),同时仍然与数据库交互。目前,我将API设置为每个API请求返回100个对象。问题是我的性能受到了巨大冲击。它可以采取> 4秒导入所有数据。大部分时间都是在调用save函数时花掉的。我已成功将导入推送到后台线程。但是当我在每个api请求结束时将主上下文保存在主线程上时,可能需要1秒以上才能保存,这会暂停所有滚动,动画等。我可以做什么来保存主要上下文的背景?

更新:以下是一些示例代码

-(void) importLoop:(NSManagedObjectContext*)mainContext complete:(dispatch_block_t) complete{
    [self apiRequest:^(NSArray *objects) {
        if (objects.count == 0){ // nothing to load.
            dispatch_async(dispatch_get_main_queue(), complete);
            return;
        }
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
        dispatch_async(queue, ^{
            NSManagedObjectContext* bgContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
            [bgContext setParentContext:mainContext];

            [bgContext performBlock:^{
                for (id object in objects){
                    // import data. This can take > 4 seconds
                    [bgContext save:nil]; // This can take > 1 second
                    // Note: This save merges the data to the main moc... but does not save the data to disk
                }
                [mainContext performBlock:^{
                    [mainContext save:nil]; // This can take > 1 second on main thread
                    [self importLoop:mainContext complete:complete]; // import next batch
                }];
            }];
        });
    }];
}

更新  根据@Wain answer,我需要设置以下内容

mainMoc - > mainThreadMoc - > bgMoc

哪里

  • mainMoc具有并发类型NSPrivateQueueConcurrencyType(此moc用于保存到磁盘)
  • mainThreadMoc具有并发类型NSMainQueueConcurrencyType并且是mainMoc的子级
  • bgMoc具有并发类型NSPrivateQueueConcurrencyType并且是mainThreadMoc的子项

好了,我已经有了这个设置,我有以下问题...当我对mainThreadMoc进行更改时,我如何让bgMoc知道它。我有以下为NSManagedObjectContextDidSaveNotification

注册的功能
- (void)managedObjectContextDidSaveNotification:(NSNotification *)notification
{
    NSManagedObjectContext *savedContext = [notification object];

    // ignore change notifications for the main MOC
    if (self.mainMoc == savedContext)
    {
        return;
    }

    if (self.mainMoc.persistentStoreCoordinator != savedContext.persistentStoreCoordinator)
    {
        // that's another database
        return;
    }
    if (savedContext == self.mainThreadMoc){
        [self.mainMoc performBlock:^{
            [self.mainMoc mergeChangesFromContextDidSaveNotification:notification];
        }];
    }else if (savedContext == self.bgMoc){
        [self.mainThreadMoc performBlock:^{
            [self.mainThreadMoc mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
}

更新:我发现上面提到的上下文链接,如果我像在初始示例中那样创建背景上下文,那么事情似乎正在发挥作用。保存后台线程时仍有明显的延迟(我认为这是将更改传播到主线程的结果)。然而,这是性能的巨大改进。

2 个答案:

答案 0 :(得分:2)

我不会通过引入额外的私有队列父MOC来开始。

充其量它会略微减少在主线程上花费的总时间,同时增加完成导入的总挂钟时间。

我还不建议在核心数据之上建立第三方框架,而您仍然需要掌握核心数据框架。

关于处理NSManagedObjectContextDidSaveNotification的问题,如果您在第一个示例中显示的每个摄取块临时创建了新的bgContext,则无需处理这些通知所有这些都是因为在子上下文中立即调用save:并透明地将更改传播到父级。

至于原始问题和示例,对于100个对象,调用save:不应该花费1秒钟。

在了解为什么需要1秒钟来保存100个对象之前,您不应该考虑架构更改(例如不同的上下文安排)。

将您的应用程序连接到乐器并让告诉您一直在占用什么。答案可能让你大吃一惊。

答案 1 :(得分:0)

您需要保存到持久性存储中,而不一定是从主上下文中保存。

您应该有一个私有队列上下文,它是主上下文的父级和后台上下文,因此您可以在不影响主线程的情况下进行保存,然后将更改合并到主上下文中。主要是指主线程,而非主要所有权。