私たちは、コア データを使用して数万のオブジェクトを格納するエンタープライズ レベルのアプリケーションに取り組んでおり、いくつかの面で問題を抱えています。
私たちのアプリケーションには、必要に応じてデータを操作するいくつかの独立したシステムがあります。これらのシステムには、アイテムの検出、アイテムのロード、同期、および UI 表示が含まれます。ソフトウェアを正しく設計すれば、異なるシステムが同じオブジェクトを変更することによるマージの競合はほとんどまたはまったくないはずです。各システムには独自の操作キューがあり、すべてバックグラウンドで実行されます。UI パフォーマンスの問題を最小限に抑えるために、すべてのオブジェクトの作成と変更をバックグラウンドに保持したいと考えています。特に、サーバー上のデータから何千ものオブジェクトが作成される可能性がある初期のランプアップ中はそうです。ここで、さまざまな設計の試みでいくつかの問題にぶつかりました。これらのランプアップ中の大量のメモリ消費、およびすべてのコンテキストと子コンテキストの不適切なオーケストレーションにより、デッドロックとクラッシュが発生します。以下の設計を試みました。
- 1 つの子コンテキストを持つ 1つのルート
NSPrivateQueueConcurrencyType
管理オブジェクトNSMainQueueConcurrencyType
コンテキスト。UI フェッチ結果コントローラーは、この子コンテキストを使用して結果をフェッチします。子コンテキストから、 「savingContext」と呼ばれるNSMainQueueConcurrencyType
1NSPrivateQueueConcurrencyType
つの子コンテキストを作成し、各バックグラウンド操作によってその「savingContext」の子コンテキストが作成され、その変更が行われ、最後に「ディープ セーブ」と呼ばれるものが実行され、再帰的に保存されます。トップ。最初にこの設計を選択したのはNSManagedObjectContextDidSaveNotification
、多くの異なる子コンテキストからの通知を処理する必要がないようにするためです。NSPrivateQueueConcurrencyType
コンテキストへのすべての呼び出しとオブジェクトへのアクセスをperformBlockAndWait:
. 機能的に、このデザインは機能しました。すべての変更と挿入は永続ストアに保存され、変更によって UI が更新されました。これにより、2 つの問題が発生しました。1 つは、マージされた変更がNSMainQueueConcurrencyType
子コンテキストを通過するため、ランプアップ中に UI の遅延が発生し、さらに重要なことに、ランプアップ中のメモリ使用量が非常に高かったことです。reset
コンテキストで再帰的に呼び出すことができないため (メイン UI の子コンテキストも存在するため)、および/または を呼び出すタイミングに関する知識が不足しているため、RAM の使用量が極端に多くなりますrefreshObject:mergeChanges:
。そこで私たちは別の道を歩みました。 - 永続ストア コーディネーターにリンクされた 2 つのトップレベル コンテキストを用意します。1
NSPrivateQueueConcurrencyType
つは子コンテキストの保存用、もう 1 つNSMainQueueConcurrencyType
は UI 表示用です。はメイン コンテキストからの通知をNSMainQueueConcurrencyType
リッスンし、それらをメイン スレッドにマージします。各バックグラウンド操作は、メインコンテキストの子コンテキストを作成し、これもプライベート キュー同時実行タイプを使用して実行し、現在のコンテキストで保存を実行する「ディープ セーブ」を再帰的に実行し、親へのディープ セーブの再帰呼び出しを実行します。 、現在のコンテキストでリセットを呼び出し、再度保存します。このようにして、作成されたオブジェクトは保存後にすぐに解放されるため、メモリの問題を回避できます。ただし、この設計では、デッドロック、NSManagedObjectContextDidSaveNotification
NSPrivateQueueConcurrencyType
NSPrivateQueueConcurrencyType
NSInternalInconsistencyException
NSMainQueueConcurrencyType
コンテキストの保存通知があるにもかかわらず、例外とフェッチされた結果コントローラーが UI を更新しない。これにより、UI の初期ロード時間が大幅に遅くなります。以前の設計では、フェッチされた結果コントローラーは非常に高速に結果を返しましたが、ビューが読み込まれるまで UI が数秒間ブロックされていました (フェッチされた結果コントローラーを で初期化しますviewDidLoad
)。
多くの中間設計を試しましたが、それらはすべて同じ問題を中心に展開しています。メモリ使用量が非常に多い、フェッチされた結果コントローラーが UI を更新しない、またはデッドロックとNSInternalInconsistencyException
例外です。
私は本当にイライラしています。私たちのデザインは、かなりシンプルであるべきものに対して、あからさまに複雑であるかのように感じずにはいられません。私たちを殺しているのは、基本的なことを理解していないためです。
それで、あなたたちは何を提案しますか?私たちのコンテキストにはどのような配置をお勧めしますか? 異なるスレッドで異なるコンテキストをどのように管理する必要がありますか? 挿入されたオブジェクトを解放し、コンテキストをリセットするためのベスト プラクティスは? デッドロックを回避しますか? この時点で、すべての助けをいただければ幸いです。
MagicalRecords カテゴリの推奨事項も確認しました。おすすめですか?Core Data タイプの使用にすでに投資していますが、MR を使用して移行するのはどのくらい難しいでしょうか?