2014-03-06 55 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秒鐘長的尖峯? –