0

我想獲得以下工作。UIManagedDocument與NSFetchedResultsController和背景上下文

我有一個表格視圖顯示從表格視圖中的API獲取的數據。爲了這個目的,我使用一個NSFetchedResultsController:

self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request 
                     managedObjectContext:self.database.managedObjectContext 
                     sectionNameKeyPath:nil 
                       cacheName:nil]; 

我在這樣的背景環境中創建我的實體:

NSManagedObjectContext *backgroundContext; 
    backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
    backgroundContext.parentContext = document.managedObjectContext; 

    [backgroundContext performBlock:^{ 
     [MyAPI createEntitiesInContext:backgroundContext]; 

     NSError *error = nil; 
     [backgroundContext save:&error]; 
     if (error) NSLog(@"error: %@",error.localizedDescription); 

     [document.managedObjectContext performBlock:^{ 
      [document updateChangeCount:UIDocumentChangeDone]; 
      [document.managedObjectContext save:nil]; 
     }]; 

現在,每當我得到新的數據(和插入/更新實體,如所示只上面),我的NSFetchedResultsController不像它應該那樣工作。特別是,我總是更新一個實體(而不是創建一個新實體),但我的表視圖顯示了兩個實體。一旦我重新啓動應用程序,它顯示正確。

如果我在self.database.managedObjectContext中執行實體的創建([MyAPI createEntities]),一切正常。

任何想法我做錯了什麼?仔細觀察SO上的現有線索,我認爲我正在以正確的方式進行。再次,如果我不做核心數據保存在後臺上下文(但在document.managedObjectContext),那麼它工作正常...

回答

1

我今天在Apple dev論壇上看到類似的問題。也許這和你的問題是一樣的,https://devforums.apple.com/message/666492#666492,在這種情況下可能有一個錯誤(或者至少有其他人有同樣的問題來討論它!)。

假設不是這樣,這聽起來像你想要做的事情應該完全可能與嵌套上下文,因此假設沒有錯誤UIManagedDocument

我唯一保留的是,我一直試圖讓一批裝載有UIManagedDocument工作,好像它不與嵌套上下文(https://stackoverflow.com/q/11274412/1347502)工作。我認爲NSFetchedResultsController的主要優點之一是它可以通過批量加載來提高性能。所以,如果這不能在UIManagedDocument中完成,或許NSFetchedResultsController尚未準備好與UIManagedDocument一起使用,但我還沒有完成該問題的底部。

除了保留,大部分我已閱讀或查看有關嵌套上下文和後臺工作的說明似乎都是通過對等子上下文完成的。你所描述的是父母,孩子,孫子的配置。在WWDC 2012的視頻「會話214 - 核心數據的最佳做法」(+ 16:00分鐘),蘋果公司建議增加此方案的父上下文另一個對等情況下,如

backgroundContext.parentContext = document.managedObjectContext.parentContext; 

工作在這個異步執行上下文,然後通過調用保存在背景上下文中推送給父代。然後父節點將異步保存,任何對等上下文(本例中爲document.managedObjectContext)都將通過讀取,合併或刷新來訪問更改。這也是UIManagedDocument文檔中描述:

  • 如果合適的話,可以從後臺線程直接 加載數據到父上下文。您可以使用parentContext獲取父上下文。將數據加載到父上下文意味着你不會干擾子上下文的操作。您可以通過執行提取來檢索在後臺加載的數據 。

[編輯:重新閱讀這一點,可能只是建議傑弗裏的建議,即不產生在所有任何新的情況和只使用父上下文]

這樣說的文檔還建議通常你不會調用保存子上下文,但使用UIManagedDocument的保存方法。這可能是您確實要求保存或可能是問題的一部分的場合。如Jeffery所述,更強烈地阻止調用保存父上下文。我讀過堆棧溢出的另一個答案,建議只使用updateChangeCount來觸發UIManagedDocument保存。但我沒有從Apple那裏讀過任何東西,所以在這種情況下,調用UIManagedDocument saveToURL:forSaveOperation:completionHandler:方法可能適合讓所有內容同步並保存。

我猜測下一個明顯的問題是如何通知NSFetchedResultsController已經發生變化。我會被誘惑簡化設置上面所討論的,然後訂閱各種NSManagedObjectContextObjectsDidChangeNotification或保存在不同的情況下通知,並看到當UIMangedDocument保存,如果有的話,是所謂的,自動保存,或者在後臺更改保存到父(假設在這種情況下是允許的)。我假設NSFetchedResultsController已連線到這些通知,以便與底層數據保持同步。

或者,您可能需要在主要上下文中手動執行提取,合併或刷新以獲取更改,然後以某種方式通知NSFetchedResultsController它需要刷新?

個人而言,我不知道是否UIManagedDocument準備用於一般消費,今年出現以及如何建立一個更復雜的解決方案,而不是一個漫長的討論是在WWDC沒有提到它提出:「會話227 - 使用的iCloud核心數據」

0

因爲你在不同的上下文更新結果,我認爲你將需要在您的視圖控制器-viewWillAppear:方法中調用[self.fetchedResultsController performFetch:&error]


更新

確定後,你不應該調用[backgroundContext save:&error][document.managedObjectContext save:nil]。請參閱:UIManagedDocument Class Reference

您通常應使用標準的UIDocument方法來保存文檔。 如果直接保存子上下文,則只會將更改提交到父上下文,而不是提交給文檔存儲。如果直接保存父上下文,則會避開文檔執行的其他重要操作。

我不得不使用-insertedObjectsobtainPermanentIDsForObjects:error:來堅持在上下文中創建的新對象。

接下來,我不認爲你需要創建一個新的上下文在後臺運行。 document.managedObjectContext.parentContext應該是可用的後臺上下文來運行更新。

最後,我並不經常撥打[document updateChangeCount:UIDocumentChangeDone]。這由文檔自動處理。你仍然可以隨時做到這一點,但它不應該是必要的。

以下是我如何打電話給您的-createEntitiesInContext方法。

[document.managedObjectContext.parentContext performBlock:^{ 
    [MyAPI createEntitiesInContext:document.managedObjectContext.parentContext]; 

    NSSet *objects = [document.managedObjectContext.parentContext insertedObjects]; 
    if (objects.count > 0) { 
     NSError *error = nil; 
     [document.managedObjectContext.parentContext obtainPermanentIDsForObjects:objects error:&error] 
     if (error) NSLog(@"error: %@",error.localizedDescription); 
    } 
}]; 
+0

嗯,我認爲不同上下文中的更改將被合併,所以fetchedResultsController將最終得到通知。無論如何,我試着在backgroundContext保存後調用[self.fetchedResultsController performFetch:nil],但仍然是相同的情況:我的表視圖顯示兩個實體,當只有一個實體時。重新啓動應用程序後,它工作正常。 – user1013725 2012-07-05 20:44:29

+0

還記得我有一個UIDocument,也許這改變了事情?我基本上想在後臺線程中將新實體保存/更新到我的文檔中。如果我在UIDocument的managedObjectContext上執行保存,則UI滯後。這就是爲什麼我試着用backgroundContext來做這件事,但不知何故,事情會變得混亂/不合並/不知道發生了什麼。我現在變得非常絕望。 – user1013725 2012-07-05 20:59:39

+0

不幸的是,你的代碼仍然不適合我。它給了我完全奇怪和不可預知的結果。什麼樣的工作對我來說是使用後臺上下文,然後調用保存並獲得該後臺上下文的永久ID。但是,如果更新代碼被同時調用兩次,我現在遇到了競爭條件問題,我認爲這是因爲它在兩個不同的上下文中創建實體,然後合併它們。不知道如何防止這種情況。 – user1013725 2012-07-11 06:28:39

1

在我的方法,我從服務器獲取數據,我首先創建實體之後,我調用這兩個方法來保存對文檔的更改:

[self.managedObjectContext performBlock:^{ 
    // create my entities 


    [self.document updateChangeCount:UIDocumentChangeDone]; 
    [self.document savePresentedItemChangesWithCompletionHandler:^(NSError *errorOrNil) { 
      ... 
     }]; 
}];