2014-03-06 32 views
0

我的应用程序需要从Web API(通常超过5000个对象)导入对象,同时仍与数据库交互。目前我的API设置为每个API请求返回100个对象。问题是我有巨大的表现。可能需要4秒钟才能导入所有数据。大部分时间都是在调用save函数时花费的。我已经成功地将导入推入后台线程。但是,当我在每个API请求结束时将主环境保存在主线程中时,可能需要1秒钟以上的时间才能保存,这会暂停所有滚动,动画等。我可以如何将主环境保存到的背景?ios核心数据导入大量数据

更新时间:下面是一些示例代码

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

更新以下功能:我发现,与上下文链接正如我上面,如果提到我重新创建背景方面,正如我在最初的例子做接下来的事情似乎正在工作。当我保存后台线程时(仍然会有明显的延迟)(我认为这是将更改传播到主线程的结果)。然而这是一个很大的性能改善。

+0

为什么不直接调用与导入相同的后台线程保存? – brindy

+0

从我所了解的Core Data处理过程中,不应该在不同的线程上使用相同的托管对象上下文。保存孩子moc将数据合并到主moc中。但是,要保存到磁盘,您需要保存主要的moc。真正的问题是,我仍然可以在导入过程中与主要moc进行交互,并将其保存在另一个线程中可能会导致一些主要问题 – datinc

+0

您的主要上下文是直接连接到持久存储协调器还是它也有父上下文? –

回答

0

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

你应该有其父母是你的主要背景和后台前后,因此您可以在不影响主线程保存,然后将更改合并到主背景下的专用队列上下文。主要是指主线程,而不是主线程。

+0

考虑使用像RestKit这样的框架,它将为您配置上下文并帮助在后台加载数据。 – Wain

+0

所以...我需要制作一个具有并发类型NSPrivateQueueConcurrencyType的主要moc。然后给它一个并发类型为NSMainQueueConcurrencyType的子类moc。然后保存我需要先保存主线程moc,然后保存主moc(在后台线程上)。那么这并不复杂。此外,如果我使用主要的moc做我的后台导入,那么主线程moc中会看到这些更改吗?或者我需要制作另一个背景moc并将其父级设置为主线程moc? – datinc

+0

不要使用主MOC,它只是在那里协调。你应该真的使用一个框架来为你管理... – Wain

2

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

充其量将略有减少花费在主线程中的总时间量,同时增加总挂钟时间来完成导入。

我也不会建议对核心数据之上的第三方框架,而你都还需要与核心数据框架本身交手。

至于你提到处理NSManagedObjectContextDidSaveNotification,如果一个新bgContext暂时会为每个摄入块,你在你的第一个例子显示的问题,就没有必要在处理这些通知都是因为一个孩子叫​​上下文立即并透明地将更改传播给父级。

至于原来的问题和例子,调用​​不应该花1秒100个对象。

直到你明白为什么需要1秒才能保存100个对象,你不应该考虑架构变化(如不同的上下文安排)。

把你的应用挂在乐器上,让it告诉你所有的时间都在用什么。答案可能会让你大吃一惊。

+0

我附加了仪器的功能,罪犯是对数据库的请求(获取或创建类型调用)和保存。感谢有关保存的数据自动传播的提示。 – datinc

+0

我还添加了代码来计算这些长时间运行的函数以验证它们花了多长时间。 – datinc

+0

好的,所以在我提出建议之前,我需要多一点清晰。你只是在处理在主线程上花费的累计时间,或者是否存在像'save:'这样的实际1秒钟长的尖峰? –