私は初めての iOS アプリを作成しています。理論的には非常に簡単なはずですが、App Store に自信を持って提出できるほど十分な防弾を実現するのに苦労しています。
簡単に言うと、メイン画面にはテーブル ビューがあり、行を選択すると、選択した行に関連する情報をマスター/詳細形式で表示する別のテーブル ビューに切り替わります。基になるデータは、Web サービスから 1 日に 1 回 JSON データとして取得され、コア データ ストアにキャッシュされます。その日より前のデータは削除され、SQLite データベース ファイルが無限に大きくなるのを防ぎます。すべてのデータ永続化操作は、Core Data を使用して実行されNSFetchedResultsController
、詳細テーブル ビューを支えます。
私が見ている問題は、新しいデータが取得、解析、および保存されている間にマスター画面と詳細画面を数回すばやく切り替えると、アプリがフリーズまたは完全にクラッシュすることです。メインスレッドがフェッチを実行しようとしている間にコアデータがバックグラウンドでデータをインポートしているために、ある種の競合状態があるようですが、私は推測しています. 意味のあるクラッシュ情報を取得するのに苦労しました。通常、それは Core Data スタックの奥深くにある SIGSEGV です。
以下の表は、詳細テーブル ビュー コントローラーが読み込まれたときに発生するイベントの実際の順序を示しています。
メイン スレッド バックグラウンド スレッド viewDidLoad JSON データを取得する (AFNetworking を使用) 子 NSManagedObjectContext (MOC) を作成する JSON データを解析する 子 MOC に管理対象オブジェクトを挿入する 子 MOC を保存 インポート完了通知の投稿 インポート完了通知を受け取る 親MOCを保存 フェッチを実行してテーブル ビューをリロードする 子 MOC の古い管理対象オブジェクトを削除する 子 MOC を保存 投稿削除完了通知 削除完了通知を受け取る 親MOCを保存
JSON データが到着したときに AFNetworking 完了ブロックがトリガーされると、入れ子NSManagedObjectContext
が作成され、JSON データを解析してオブジェクトをコア データ ストアに保存する「インポーター」オブジェクトに渡されます。インポーターはperformBlock
、iOS 5 で導入された新しいメソッドを使用して実行されます。
NSManagedObjectContext *child = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[child setParentContext:self.managedObjectContext];
[child performBlock:^{
// Create importer instance, passing it the child MOC...
}];
インポーター オブジェクトは、独自の MOC を監視しNSManagedObjectContextDidSaveNotification
、詳細テーブル ビュー コントローラーによって監視される独自の通知を投稿します。この通知が送信されると、Table View Controller は独自の (親) MOC で保存を実行します。
その日の新しいデータがインポートされた後に古いデータを削除するために、「deleter」オブジェクトで同じ基本パターンを使用します。これは、フェッチされた結果コントローラーによって新しいデータがフェッチされ、詳細テーブル ビューが再ロードされた後、非同期に発生します。
私が行っていないことの 1 つは、マージ通知を観察したり、管理対象オブジェクト コンテキストまたは永続ストア コーディネーターをロックしたりすることです。これは私がすべきことですか?これをすべて正しく設計する方法が少しわからないので、アドバイスをいただければ幸いです。