今まであまりCoreData周りのライブラリ使ったことなかったのですが、今回MagicalRecordを利用することになりました。
ライブラリの機能が豊富でドキュメントを見てもわからず、ちょっとずつ異なるメソッド名を見てもどういうときに使うのかわからなかったので調べてみました
前提
NSManagedObjectContext(オブジェクトの管理スペース)のContextType
Method | 説明 |
---|---|
NSConfinementConcurrencyType |
スレッド拘束パターン、 コンストラクタにタイプを指定しないで生成した場合のデフォルト |
NSPrivateQueueConcurrencyType |
private dispatch queueを使用する。バッチ処理や永続化処理に向く |
NSMainQueueConcurrencyType |
Main Queueを使用する。Viewの描画が固まってしまうので、大量のデータをこのContextで処理するのは避けるべき |
基本的に中規模以上のアプリは、NSManagedObjectContextを複数生成して使用します。
MagicalRecordを使うと、NSManagedObjectContextの生成とPublish/Subscribe(NSNotificationCenterなど)の登録、NSManagedObjectContextへの保存、永続化処理を簡単に行えるようです。
MagicalRecordのsetupメソッドで生成されるNSManagedObjectContext
Context | 説明 |
---|---|
MR_rootSavingContext |
|
MR_defaultContext |
|
ざっくりとした理解だと、MR_rootSavingContextの方はデータの永続化、MR_defaultContextはデータの表示・更新などで利用するような感じです。
保存メソッド
最初に混乱するのが、NSManagedObjectContext (MagicalSaves)
とMagicalRecord (Actions)
の両方に保存メソッドがあることです。
-
NSManagedObjectContext (MagicalSaves)
に定義されているメソッドは、NSManagedObjectContext
への保存または永続化処理 -
MagicalRecord (Actions)
に定義されているメソッドは、NSManagedObjectContext
を新たに生成または取得してNSManagedObjectContext (MagicalSaves)
に定義されているメソッドを呼び出している
MagicalRecord (Actions)
やっていることは下のような処理です。
- contextを生成
- 1.で生成したものをBlocksで渡して、アプリ側でそのContextを使ってデータ作成・更新処理などが行えるコールバック呼び出し
-
NSManagedObjectContext (MagicalSaves)
に定義されているメソッドを呼び出し、データの永続化まで行う
contextの生成・取得はメソッドが自動でやってくれるため、アプリ側ではそのcontextを使ってコールバックに必要なデータの作成・更新の処理を書くだけです。
非同期呼び出し処理タイプ
Method | 説明 |
---|---|
+ (void)saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block; |
下のメソッドをcompletionBlocksがnilで呼び出し |
+ (void)saveWithBlock:(void(^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion; |
親のcontextとしてMR_rootSavingContextを指定して、NSPrivateQueueConcurrencyTypeのcontextを作成します |
+ (void)saveUsingCurrentThreadContextWithBlock:(void (^)(NSManagedObjectContext *localContext))block completion:(MRSaveCompletionHandler)completion; |
|
実行完了待ちタイプ
上の実行完了待ちバージョン
+ (void) saveWithBlockAndWait:(void(^)(NSManagedObjectContext *localContext))block;
+ (void) saveUsingCurrentThreadContextWithBlockAndWait:(void (^)(NSManagedObjectContext *localContext))block;
NSManagedObjectContext (MagicalSaves)
MagicalRecord (Actions)
に定義されたメソッドを利用すると、contextの生成からデータの永続化まで自動で行ってくれますが、それでは困ることがあります。
- ローカルの登録フォームからデータを生成したが、APIでサーバーにリクエストを送ったらエラーが返ってきた。
- 画面を閉じる前に保存したい(保存タイミングの制御)
- 複数のAPIからデータを取得して、全部揃ったタイミングで保存
1.の場合は、サーバーに保存できるまでCoreDataで管理するデータを生成しないという選択肢がありますが、スコープを超えてプリミティブなデータを持っておくのはダルいです。
それよりも、専用のcontextを生成して、サーバーに保存が成功したらローカルに保存・永続化、保存失敗したらcontextごとなかったことにするとかの方が簡単です。
あと、一回一回いちいち永続化するのではなく、一連の処理が完了したら永続化したいところです。
定義されている保存メソッドは下記の通りです。
- (void) MR_saveOnlySelfWithCompletion:(MRSaveCompletionHandler)completion;
- (void) MR_saveToPersistentStoreWithCompletion:(MRSaveCompletionHandler)completion;
- (void) MR_saveOnlySelfAndWait;
- (void) MR_saveToPersistentStoreAndWait;
- (void) MR_saveWithOptions:(MRSaveContextOptions)mask completion:(MRSaveCompletionHandler)completion;
メソッド名にWait
が入っている場合は、実行完了まで待つタイプで、ToPersistentStore
が入っているのは、ParentContextをさかのぼって保存するタイプです。さかのぼったcontextにpersistentStoreが接続されていれば永続化処理が行われます。
あと、saveOnlySelf
が入っているものは、そのcontext自身にのみ保存するタイプです。
上のメソッドは、自分自身のみかルートとなるcontextまでさかのぼって保存するしかないため、
もっと細かく保存するcontextやタイミングを指定したい場合は、MagicalRecordに頼らずに、CoreDataフレームワークで用意されているメソッドを使うしかないです。
ということで、何を調べていたかというと既存の処理が重いのでどのメソッドを実行しているときに重いのかを調査したかっただけです。