2010-07-04 46 views
1

簡單,常見的模式,我不能在蘋果的文檔發現:如何製作/使用臨時NSManagedObjects?

  1. 加載一個coredata店
  2. 下載新的數據,在內存中創建對象
  3. 保存一些的新數據(通常「只有新位/沒有改變的位」)

取而代之,我可以找到這些替代方法,其中沒有一個是正確的:

  1. 不要在內存中創建 (當然,這意味着要扔掉 一切良好對象的對象。 使用大量 編寫您的代碼NSDictionary的服務沒有目的 除了解決方法CoreData的 失敗。一般不可行)
  2. 創建對象,但隨後刪除 那些你不想(蘋果建議在文檔 這一點,但他們的通知去可怕的錯誤 :那些「刪除」顯示當 您嘗試保存,即使他們 不應該/不)
  3. 在二級 環境中創建對象(蘋果強烈暗示這 是正確的,但顯然對於 不提供任何方式,你從臨時 移動對象沒有 這樣做(刪除剛纔創建的對象 ,然後做一個 保存)。一般情況下這是不可能的,因爲對象通常需要在新的上下文中被引用,並且保存將失敗)

當然,這不應該是這麼困難嗎?

如果我必須編寫所有代碼來手動深度複製一個對象(通過迭代它的所有字段和數據結構),爲什麼CoreData首先存在?這是CD在內部提供的基本功能。

我迄今爲止工作的唯一解決方案是選項2(來自蘋果的文檔),自定義啓發式方法可以「猜測」蘋果發送NSNotifications用於永遠不應該保存的對象(但Apple無論如何發送notofications)。這是一個可怕的黑客攻擊。

編輯:澄清:

我無法弄清楚如何獲得蘋果的通知才能正確地傳送。 Apple的代碼似乎將插入轉換爲「更新」,並將「臨時對象」轉換爲「刪除」等,我不能聽「新對象」。

回答

1

看來,選項3是最好的選擇。

編輯:在iOS 4上廣泛使用這個之後,我會說「總是使用NSOperationQueue而不是performSelectorOnBackgroundThread」。如果你不知道如何以簡單的方式使用NSOpQ,那麼可以使用它,但是它可以用少於3行的代碼完成,所以這只是使用performSel的一個小改變。它工作很多與iOS4的新線程調度程序更好。

基於「?我怎麼會這個工作」,我想出了這個辦法:

  1. 原班必須有自己的語境。它必須訂閱在其私有上下文中聽取「更改」(通過NSNotification)。
  2. 使用「performSelectorOnBackgroundThread」 ONLY調用下載方法或類似的(強迫他們去一個不同的線程)
  3. 總是傳遞參數上面的方法調用未NSManagedObjects和DO NOT refernece他們(這是通過使用強制執行選擇器...無論如何 - 但即使你在同一個線程,它會稍後擰上蘋果的代碼 - 如果你以任何其他方式)
  4. 總是提供「預先存在的」管理對象的ID,新的需要連接
  5. 始終在之前創建一個新的臨時NSManagedContext 開始下載,並且:...
  6. ...總是註冊你正在運行聽(使用NSNotifications)原始類的「拯救」這個「臨時」的背景下
  7. 做下載,創建對象,刪除那些你不想
  8. ALWAYS然後重新取(在臨時的上下文),你傳入的ID的對象,勾起來到新創建的對象
  9. 保存臨時的上下文
  10. 原班反應「上下文保存」通過重新調用回調,但在主線程(如果尚未在主線程上 - [NSThread isMainThread])
  11. ORIGINAL類,只要它在主線程上執行時,使用了「合併」的方法,從蘋果到NSNotificaiton對象合併到自己的店裏
  12. 原班反應「上下文對象改爲」通過處理變化

無論其...這還需要蘋果公司的文檔沒有提到的東西:永遠不要保存對任何託管對象的引用。EXCEPT除了引用所有其他對象的「根」對象外。

否則,蘋果公司的「合併」會嚴重破壞。

ALSO ...你可能需要手動「刺激」故障,使這項工作;有幾個關於這個問題的問題(我不知道蘋果爲什麼不自動這麼做 - 也許他們會這樣做,但如果是這樣的話,我還沒有找到實現這一目標的神奇選擇)。

我認爲還有一些其他的注意事項。如果我記得他們,我會稍後編輯它。

注意:這聽起來像是很多代碼。是的,但是......事實證明,與使用字典手動拷貝對象來試圖追蹤曲折的例子相比,它要少得多。

一旦你有了這個設置和工作,它在概念上很容易遵循。另外...如果你做全部上面的步驟,蘋果獲得NSNotifications「最」的正確。剩下的那些顯示不正確(例如一些刪除)是「如文檔中所述」。他們對我來說沒有意義,但至少這是記錄工作的方式。

+0

修改:使用OS 4,在少數情況下使用OS 3,這種情況很糟糕,除非使用NSOperationQueue而不使用performSelectorInBackground(使用隊列平滑線程 - 在OS 3上它可以防止前臺線程在OS 4上變得餓死我還不確定發生了什麼,但它感覺更平滑) – Adam 2010-07-15 10:21:04

+0

此外:我的一些「合併」問題是因爲我沒有發現(主要是未公開的)[NSManagedObject prepareForDeletion]方法;在99%的情況下,您必須*在您的應用程序中的每個對象上覆蓋該方法(蘋果公司從未在文檔中提到過這一點),以便從Apple獲得工作合併通知。否則,Apple將刪除處理合並之前需要處理的數據,然後再發送notifcatio給您! – Adam 2010-10-12 07:27:09

0

您的對象應該有一些唯一的標識符,如唯一的整數ID。這來自核心數據外部並取決於您的業務邏輯。所以當你從外面接收到一個新的對象時,你需要檢查這個ID是否已經存在於Core Data中:如果是,你編輯現有的對象;如果不是,則添加新對象。

+0

一般而言,直到創建對象後纔會知道該數據。這是標準的OOP:首先是alloc/init,其次是填寫剩餘的數據。例如,XML解析:在解析至少一些對象之後,您很少知道唯一ID(例如,查看RSS:如果ID是父節點上的屬性,那麼您的建議將起作用;相反,該ID嵌入在子節點的深處)。 – Adam 2010-07-04 11:48:47

+0

那麼......你不是直接以Core Data格式下載數據。我自己使用的模式是我將數據作爲JSON獲取,並且可以將該JSON解析爲像NSDictionary這樣的NS *對象。我可以從字典中獲取ID,然後決定是創建新的Core Data對象還是將此數據與現有對象關聯。 – Jaanus 2010-07-04 18:12:16

2

有3種常見的方法來處理這個問題。

  1. 創建具有相同的持久性存儲爲你的「真實」的情況下臨時對象上下文,你的對象添加到這個臨時的上下文,一旦你知道你要保留的對象,從臨時刪除所有其他上下文並保存臨時上下文。保存時,可以通過觀察NSManagedObjectContextDidSaveNotification通知並將其合併到「真實」上下文(ala [realContext mergeChangesFromContextDidSaveNotification:notification])中來更新「真實」上下文。有關詳細信息,請參閱Mike Weller的回答here

    (如果你很在意I/O,你可以使用一個在內存方面,它有優點和缺點。)

  2. 而不是使用NSManagedObject的,使用一個NSDictionary。一旦你知道你想保留哪些對象,實例化一個新的託管對象並調用[managedObject setValuesForKeysWithDictionary:temporaryObject]將臨時對象中的值複製到託管對象中,然後保存「真實」上下文。如果您的代碼需要與NSManagedObjects和臨時對象(例如表視圖)一起使用,那麼您可以使用鍵值編碼(又名valueForKey:,setValue:forKeyPath :)編寫該代碼。

  3. 將「isTemporary」屬性添加到您的實體模型(默認爲NO)。當你創建一個臨時對象時,將isTemporary設置爲YES並將該對象插入到你的「真實」上下文中。一旦你知道你想保留哪些對象,將他們的isTemporary屬性改爲NO。當然,您需要定期刪除這些臨時對象,但這很容易實現(例如,該任務完成時,應用程序退出等)。

的#1和#3的優點是,你的對象居住在CoreData世界 - 例如,它們可以查詢,他們可以參與的關係,等等#2的優點是,它的光並且速度很快,特別是如果您有很多臨時對象。