簡體   English   中英

同時可靠地使用核心數據

[英]Using Core Data Concurrently and Reliably

我正在構建我的第一個iOS應用程序,理論上應該非常簡單,但我很難讓它足夠防彈,讓我有信心將它提交到App Store。

簡而言之,主屏幕具有表格視圖,在選擇行時,它將分段到另一個表格視圖,該表格視圖以主 - 細節方式顯示與所選行相關的信息。 基礎數據每天從Web服務檢索為JSON數據,然后緩存在Core Data存儲中。 刪除當天之前的數據以阻止SQLite數據庫文件無限增長。 所有數據持久性操作都使用Core Data執行, NSFetchedResultsController支持詳細信息表視圖。

我看到的問題是,如果你在主屏幕和細節屏幕之間快速切換幾次,同時檢索,解析和保存新數據,應用程序會凍結或完全崩潰。 似乎存在某種競爭條件,可能是由於Core Data在后台導入數據而主線程正在嘗試執行獲取,但我猜測。 我在捕獲任何有意義的崩潰信息時遇到了麻煩,通常它是Core Data堆棧中的一個SIGSEGV。

下表顯示加載詳細信息表視圖控制器時發生的事件的實際順序:

Main Thread                          Background Thread
viewDidLoad

                                     Get JSON data (using AFNetworking)

Create child NSManagedObjectContext (MOC)

                                     Parse JSON data
                                     Insert managed objects in child MOC
                                     Save child MOC
                                     Post import completion notification

Receive import completion notification
Save parent MOC
Perform fetch and reload table view

                                     Delete old managed objects in child MOC
                                     Save child MOC
                                     Post deletion completion notification

Receive deletion completion notification
Save parent MOC

在JSON數據到達時觸發AFNetworking完成塊后,將創建嵌套的NSManagedObjectContext並將其傳遞給“導入器”對象,該對象解析JSON數據並將對象保存到Core Data存儲。 導入器使用iOS 5中引入的新performBlock方法執行:

NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [child setParentContext:self.managedObjectContext];        
    [child performBlock:^{
        // Create importer instance, passing it the child MOC...
    }];

導入器對象觀察其自己的MOC的NSManagedObjectContextDidSaveNotification ,然后發布自己的通知,該通知由詳細信息表視圖控制器觀察。 發布此通知時,表視圖控制器會在其自己的(父)MOC上執行保存。

我使用與“刪除”對象相同的基本模式,在導入當天的新數據后刪除舊數據。 在獲取的結果控制器提取新數據並重新加載詳細信息表視圖之后,這會異步發生。

我沒做的一件事是觀察任何合並通知或鎖定任何托管對象上下文或持久性存儲協調器。 這是我應該做的事嗎? 我有點不確定如何正確地構建這一切,所以將不勝感激任何建議。

在iOS 5之前,我們通常有兩個NSManagedObjectContexts :一個用於主線程,一個用於后台線程。 后台線程可以加載或刪除數據然后保存。 然后,生成的NSManagedObjectContextDidSaveNotification (正如您所做的那樣)傳遞給主線程。 我們調用mergeChangesFromManagedObjectContextDidSaveNotification:將它們帶入主線程上下文。 這對我們來說效果很好。

這方面的一個重要方面是save:在后台線程上阻塞,直到mergeChangesFromManagedObjectContextDidSaveNotification:在主線程上完成運行(因為我們從偵聽器調用mergeChanges ...到該通知)。 這可確保主線程管理對象上下文看到這些更改。 如果您有親子關系,我不知道您是否要這樣做,但是您在舊模型中做了以避免各種麻煩。

我不確定在兩個上下文之間建立父子關系的好處是什么。 從您的描述看來,最終保存到磁盤發生在主線程上,這可能不是出於性能原因的理想選擇。 (特別是如果您可能正在刪除大量數據;我們的應用程序中刪除的主要成本始終發生在最終保存到磁盤期間。)

當控制器出現/消失可能導致核心數據故障時,您運行的代碼是什么? 您看到崩潰的堆棧跟蹤是什么類型的?

只是一個建築理念:

使用您聲明的數據刷新模式(每天一次,刪除和添加數據的完整周期),我實際上有動力每天創建一個新的持久性存儲(即以日歷日期命名),然后在完成通知中,表視圖設置一個新的fetchedresultscontroller與新商店(可能是一個新的MOC)相關聯,並使用它進行刷新。 然后應用程序可以(在其他地方,也許也由該通知觸發)完全破壞“舊”數據存儲。 這種技術將更新處理與應用程序當前使用的數據存儲區分開,並且“切換”到新數據可能會被視為更加原子化 ,因為更改只是開始指向新數據而不是希望你在寫入新數據時(但尚未完成),不會使存儲處於不一致狀態。

顯然我已經留下了一些細節,但我傾向於認為在使用時要更改的大量數據應該重新設計,以減少您遇到的那種崩潰的可能性。

很高興進一步討論......

我對多線程核心數據的主要問題是無意中訪問了一個線程/隊列中的托管對象,而不是它創建的那個。

我發現一個好的調試工具是添加NSAsserts來檢查在主要托管對象上下文中創建的托管對象是否僅在那里使用,而在后台上下文中創建的托管對象不在主上下文中使用。

這將涉及子類化NSManagedObjectContext和NSManagedObject:

  • 將iVar添加到MOC子類並為其分配創建的隊列。
  • 您的MO子類應該檢查當前隊列是否與其MOC的隊列屬性相同。

它只是幾行代碼,但長期可以防止你制作難以追蹤的錯誤。

NSFetchedResultsController已被證明對大量刪除有點敏感,因此我將首先開始挖掘。

我的初步問題是,如何重新獲取和重新加載tableview與刪除操作的開始有關。 NSFetchedResultsController仍在提取或沒有時,刪除塊是否有可能保存子MOC?

當您從詳細視圖切換到主視圖然后返回到詳細視圖時,是否可能會運行多個並發后台任務? 或者您是否一次從Web服務檢索所有數據,而不僅僅是與特定行相關的數據?

使其更加健壯的一種替代方法是使用類似於UIManagedDocument使用的模式:

UIManagedDocument實際上將主MOC創建為私有隊列,並使子MOC可用於主線程,而不是使用父MOC作為主線程並發類型。 這里的好處是所有I / O在后台繼續進行並保存到父MOC根本不會干擾子MOC,直到明確地讓兒童MOC了解它們。 那是因為保存提交從子節點變為父節點而不是相反。

因此,如果您在私有的父隊列上執行了刪除操作,那么根本不會在NSFetchedResultsController范圍內進行刪除。 由於它是舊數據,這實際上是首選方式。

我提供的另一種選擇是使用三種上下文:

主要MOCNSPrivateQueueConcurrencyType

  • 負責持久存儲和刪除舊數據。

Child MOC ANSMainQueueConcurrencyType

  • 負責與UI相關的任何事情和NSFetchedResultsController

Child MOC BNSPrivateQueueConcurrencyType ,Child MOC A的孩子)

  • 負責插入新數據並在完成后將其提交給Child MOC A.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM