ラベル Core Data の投稿を表示しています。 すべての投稿を表示
ラベル Core Data の投稿を表示しています。 すべての投稿を表示

2011年8月12日金曜日

メモ: CoreDataで更新処理をするときは、lockをわすれずに

単なるメモ書きです><

http://twitter.com/#!/akisutesama/status/83521489382555650
http://twitter.com/#!/akisutesama/status/83521729380626433
ある一つのCore Dataのモデルを非同期的に複数箇所から更新するときは、たとえどんなに軽微な、プロパティ一つだけの、他からは触られない様な変更ですら、きちんとlockを取らないと危険ということがわかった。API実行クラスだけでは不十分であった。非同期であればロック必須。
変更を行うコードブロックを渡して、内部で安全にロックして実行、必要に応じてロールバックや失敗通知も行える様にする仕組みを作ろうと思った。
CoreDataのモデルオブジェクトの更新はただのsetterプロパティの使用だけで発生してしまうのでついつい忘れがちになるのですが、これが原因で実際にクラッシュしたアプリもあるので油断禁物。

2010年5月30日日曜日

Core Data のパフォーマンスをちょっとだけ調べてみた

ちょっと仕事で触ってみて分かった範囲のことを書きます。断りがない限り、 iPhone 3GS で Wifi 接続環境下においてテストしました。

■キャッシュ無し vs キャッシュ有り

executeFetchRequest:error: メソッドを用いて、 Entityのプロパティで一件だけ絞り込んで返すようなクエリは大変遅いということが分かりました。Indexを付けて実行してもほとんど速くなりません。どうやらそもそもバックエンドに使っているSqliteが大変遅い、特にコネクションを生成したり破棄したりするのが遅い感じがするので、ループで一件ずつ取得するなどのときはたくさんのSQLが実行されないようにする必要があります。 objectWithID: メソッドは試していないのでちょっと不明です。

回避策として、アプリが起動したタイミングで当該エンティティの全オブジェクトをあらかじめ取ってきて、 NSMutableDictionary にでも突っ込んでおく。次回以降のフェッチはその NSMutableDictionary から行う、と言うようにすると凄く速くなりました。

実測値は以下の通り。
pre loading time というのが自前のNSDictionaryキャッシュの事前生成、parseResponseBodyというのがXMLの解析で、この中に大量のCore DataオブジェクトをDBから引っ張ってくる処理が含まれています。
//////////////////
プリキャッシュなし
//////////////////

2010-05-25 12:11:37.254  FetchAllModelA parseResponseBody time : -1.214856
2010-05-25 12:11:39.674  FetchAllModelB parseResponseBody time : -2.416063
2010-05-25 12:11:41.097  FetchAllModelC parseResponseBody time : -1.384185

//////////////////
プリキャッシュあり
//////////////////

2010-05-25 14:12:14.788  pre loading time : -0.039540
2010-05-25 14:12:17.518  FetchAllModelA parseResponseBody completed. time : -0.836754
2010-05-25 14:12:20.719  FetchAllModelB parseResponseBody completed. time : -1.312910
2010-05-25 14:12:19.169  FetchAllModelC parseResponseBody completed. time : -0.902832
ほんの0.03秒のプリキャッシュ処理のおかげで、XML解析が最大で1秒以上短縮できています。とにかくCore DataがSQLを飛ばさないように調整すると効果があるみたいです。


■DB書き込み速度

NSManagedObject の生成はメモリ上 (NSManagedObjectContext) で行われるためなかなか高速なのですが、それを save するのがとにかく iPhone 3G で顕著に遅く、 300件程度のデータを保存するのに5秒以上かかってアプリが正常終了できないという事態が発生しました。リレーション張りすぎたかなーと思います>< iPhone 3GS なら2秒3秒程度で間違いなく完了するので特に問題になっていません。対策としては暇なときに逐一DBにsaveするか、どうでもいいデータはiPhone 3Gでは保存しないとか、一時エンティティにするとか。

2010年5月24日月曜日

Core Data では モデルの delete - insert をしない方が良い

最近お仕事で Core Data を頻繁に使っているのですが、ちょっとだけハマったケースをご紹介します。 Core Data ではモデルの delete - insert による更新をしない方が良いようです。


■delete - insert が問題になるケース

たとえば、 Core Data から全く同じ内容をフェッチして表示する二つのUITableViewがあるとします。それぞれ A および B と名付けます。
ここで、 A を表示してから、 B を表示する際に Core Data モデルを delete - insert すると、 A を再度表示した際にアプリケーションがクラッシュします。原因はおそらく、 A で既に読み込まれているモデルを B が削除してしまっているため、 A で既に削除されているモデルをテーブルが表示しようとしてクラッシュしているものだと思われます。 A の viewWillAppear などのタイミングで、再度 Core Data からオブジェクトを取得し直せばこの問題は解決します。

そもそも NSFetchedResultsController を使えばたぶんこの問題は発生しないと思うのですが、いずれにせよ変更があったデータをフェッチしなおさなければならなくなってしまいます。ということで可能な限りプライマリキー項目を設定して update で対応することをお勧めします。ただし update は update で sqlite3 の実行がやたら遅いということが分かっているのでこちらはこちらで別途チューニングが必要です。詳細はまた別エントリーにて。