2012-07-04 23 views
15

我從Web服務器獲取數據,在名爲backgroundMOC的子級私人背景上下文中處理數據。它是鏈接到主UI的mainMOC的孩子,因此保存在backgroundMOC上會觸發UI更改。 mainMOCmasterMOC的孩子,這是一個與永久存儲綁定的私有後臺隊列,因此保存在主節點上保存到磁盤。核心數據無法在獲取PerPantmanIDs後爲對象填充故障

我現在要做的就是接收數據,在backgroundMOC創建新的對象,然後保存backgroundMOC(這樣的UI更新),保存mainMOC,(讓我幾乎可以保存到磁盤),並保存masterMOC(讓我最終可以寫入磁盤)。問題是當對象通過提取結果控制器出現在UI中時,objectId仍然是臨時的。

這會導致重複的行問題,如果我從服務器接收到相同的數據(意外),我的backgroundMOC不知道這個對象已經存在,因爲它沒有被分配一個永久ID,所以它創建另一個對象。當我重新啓動應用程序時,重複的對象消失了,所以我知道這只是id映射的一個問題。

所以我想我可能會嘗試

[backgroundMOC obtainPermanentIDsForObjects:backgroundMOC.registeredObjects.allObjects error:nil]; 

在所有儲蓄(我儲蓄過多後試過)前。然而,出於某種原因,稱此行拋出異常:

CoreData could not fulfill a fault for... 

如果您有可能導致我在正確的方向上沒有任何提示,請分享。謝謝

編輯:好吧,所以最初我在調用backgroundMOC上的obtainPermanentIDsForObjects,它是mainMOC的一個子元素,它是masterMOC的子元素。我轉換它,以便我獲得主MOC上的ID,並解決了我所有的問題(現在)。我是不是應該在子上下文中調用obtainPermIds?

+0

@Sven你爲什麼給這個獎勵呢?你有類似的問題嗎? – Snowman

+0

是的,這幾乎是我面對的同樣的問題。 – Sven

+0

我的「解決方案」是擺脫主/主/子設置,只使用主/子,以便持久性存儲鏈接到主隊列而不是背景隊列。 – Snowman

回答

19

這是一個已知的bug(嵌套的環境沒有得到時新對象將被保存永久標識)可以,並且應該被固定在即將推出的版本...

你應該能夠尋求永久的ID,雖然,但你只應該在已經插入的對象上請求它們。

[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:0]; 

必須雖然節省了MOC之前做到這一點,因爲如果你不保存永久獲得的ID,臨時ID被傳播到父上下文。例如,在您保存到mainMoc的情況下,然後獲取IDS,backgroundMOC仍然具有臨時ID,因此將來保存會創建重複數據。

請注意,獲取永久ID一直到數據庫,但是如果您在主MOC的子MOC中執行此操作,則在發生這種情況時不應阻止主線程。

因此,在您保存您最低水平MOC,你應該切實有這樣的事情(在適當的錯誤處理,當然)...

[backgroundMoc performBlock:^{ 
    [backgroundMoc obtainPermanentIDsForObjects:backgroundMoc.insertedObjects.allObjects error:0]; 
    [backgroundMoc save:0]; 
    [mainMoc performBlock:^{ 
     [mainMoc save:0]; 
     [masterMoc performBlock:^{ 
      [masterMoc save:0]; 
     }]; 
    }]; 
}]; 

還有一些其他的遊戲,你可以玩,如果你想。

提供有關NSManagedObject類似於此類別...

@implementation NSManagedObject (initWithPermanentID) 
- (id)initWithEntity:(NSEntityDescription *)entity insertWithPermanentIDIntoManagedObjectContext:(NSManagedObjectContext *)context { 
    if (self = [self initWithEntity:entity insertIntoManagedObjectContext:context]) { 
     NSError *error = nil; 
     if (![context obtainPermanentIDsForObjects:@[self] error:&error]) { 
      @throw [NSException exceptionWithName:@"CoreData Error" reason:error.localizedDescription userInfo:error.userInfo]; 
     } 
    } 
    return self; 
} 

+ (NSArray*)createMultipleObjects:(NSUInteger)count withEntity:(NSEntityDescription *)entity inManagedObjectContext:(NSManagedObjectContext *)context { 
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:count]; 
    for (NSUInteger i = 0; i < count; ++i) { 
     [array addObject:[[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context]]; 
    } 
    NSError *error = nil; 
    if (![context obtainPermanentIDsForObjects:array error:&error]) { 
     @throw [NSException exceptionWithName:@"CoreData Error" reason:error.localizedDescription userInfo:error.userInfo]; 
    } 
    return array; 
} 
@end 

現在,在第一個,你付出去到數據庫中,併爲創建的每個實體的ID,但它不是很多,它發生在後臺線程中,並且每個dip都短...

噢,它不是最好的,但它提供了有用的。另外,第二個創建同一個對象的多個,並同時獲取它們的永久ID。

您也可以使用直接連接到PSC的MOC,並觀察DidChange事件,但這與舊的方式相同。

不幸的是,你不能擁有一個單獨的MOC只是發出persistentID請求並傳遞ObjectID,儘管你可以有一個獨立的MOC在數據庫中創建原型對象,並給你它們的ObjectID。

一個原型工廠是一個相當常見的模式,如果你走這條路線,當最終的錯誤修復到達這裏時,很容易做出微小的改變。

編輯

針對斯文...

如果要創建新的,複雜的圖表,那麼你需要創建後立即獲得一個永久編號。要減少商店的點擊次數,您應該全部創建它們,然後立即獲取ID,然後開始連接它們。

老實說,所有這些都是爲了解決當前存在的錯誤,這些錯誤值得中小型的更新工作。當錯誤得到修復時,你的代碼將是相同的(無法獲得)。所以,我建議這種方法適用於較小的進口。

如果您正在進行大規模更新,我建議使用「舊」方法。創建一個直接連接到PSC的新MOC。在那裏進行所有更改,並讓您的「實時」上下文僅從這些DidSave通知合併。

最後,對永久性ID的數據庫影響。放棄MOC是可以的。磁盤被擊中,元數據被改變,但對象不被持久化。

說實話,我沒有做一個大的測試,看看是否有空的空間,所以你可能想要這樣做,並回到我身邊。

查看磁盤上的實際數據庫文件大小,然後創建10000個對象,然後獲取持久ID,釋放MOC並再次查看大小。

如果有影響,您可以嘗試刪除對象,或在大型更新後對數據庫運行真空以查看是否有效。

如果您打算創建大量可能會丟失的對象,則無需點擊數據庫。您可能希望直接附加到PSC並使用舊的忠實通知。

+0

這聽起來不錯。我試圖使用'obtainPermanentIDsForObjects:error:'來解決這個問題,這個問題實際上是有效的,但是之後導致了一個關於無法訪問的對象的異常。我想這是因爲我在獲得永久ID之前將新對象(使用臨時ID)添加到關係中。您的示例建議在創建新對象後立即獲取永久ID。我會嘗試如果這對我有用。如果與新對象的上下文永遠不會保存,您是否有任何信息? – Sven

+0

響應作爲編輯回答。 –

0

在前臺線程和後臺線程之間工作時,我遇到了各種各樣令人頭疼的問題。在尋找一個分辨率爲我一個問題我遇到

Magical Record

跑了,我花了一些時間去通過文檔和方法,我可以說,它確實令核心數據的工作容易得多。具體來說,它也將幫助您管理多個上下文和線程。

你可能想看看它。