CoreDataの恐ろしい一面を垣間見てしまったので、これはブログに書かねば!と意気込んでます。 今までCoreDataといえば
- 簡単・お手軽
- データ保護など、安全にデータを保存してくれている
- メモリ関係も自動でやってくれる
という認識でいました。 これらの利点があるから使っていました。 ・・・・ですが、CoreDataを使えば使うほどだんだんこれらの利点に疑問が出てきました。
マルチスレッドになると一気に使いにくくなる
そもそもCoreDataの危険性に気づき始めた原因がこれなんですが、 CoreDataはマルチスレッドで扱おうとすると一気に使いにくくなります。 使いにくくなるというのはコードを書く量が増えるという意味ではなく、考慮しなければならないことが多くなるという意味です。 どのスレッドがどのタイミングでデータを書き込み、それをどのスレッドがどういう形で情報を受け取るのか、などです。これが少しでも間違うとアプリが止まってしまいます。 この止まってしまうというのは、エラーを吐くのではなく、(おそらくデッドロックで)止まってしまいます。 デバッグしていろいろ調べてみた結果、スレッドAでデータを書きこもうとすると同時にスレッドBでデータを読み込もうとするだけでデッドロックが発生してしまうようです。 2本ほどCoreDataをバックグラウンドで更新するアプリを作りましたが、アプリの停止は具体的にエラーを吐くわけではないのでかなりタチが悪く、何度も悩まされています。
CoreDataで安全は存在しない
Core Dataプログラミンクグガイド(日本語版)によれば
Core Dataでは、読み込みは「安全」だが変更は「危険」、という状況になることはありません。 あらゆる操作が「危険」です。どのような操作も、キャッシュの一貫性を損ない、あるいは実体化を引き起こす可能性があるからです。
と、あらゆる動作が危険であると書いてありました。 これはおそらくDBファイルとのやり取りが発生する可能性がある以上全て危険であると言っているのだと思います。 しかし、先ほどのマルチスレッドの話じゃないですが、書き込みタイミングだけでなく読み込みタイミングも自分で把握するのはかなり難しいです。 FetchedResultsControllerなんて使ってしまったときには完全に自分で把握することはできなくなります。
実はCoreDataでは循環参照が含まれている
Objective-cを学ぶ上でdelegateというのがありました。 これはオブジェクト同士がstrongでつながり、retainCountが0にならない現象を回避するためのものでした。 ・・・・実はcoreData、このstrong同士でつながるというのをおもいっきりしています。 Memory Management of CoreData に下のように書かれています。
When you have relationships between managed objects, each object maintains a strong reference to the object or objects to which it is related. In a managed memory environment, this causes retain cycles (see “Object Ownership and Disposal”) that can prevent deallocation of unwanted objects. To ensure that retain cycles are broken, when you’re finished with an object you can use the managed object context method refreshObject:mergeChanges: to turn it into a fault.
つまり、NSManagedObject同士がrelationshipでつながっているときはstrongでつながっていて、そのstrong同士の関係を消したい時はrelease関数ではなくてrefreshObject:mergeChanges:関数を使ってくれ ということです。 CoreDataがメモリ関係もすべて管理してくれる と聞いて安心していたのですが、これを見るかぎり実はrelationshipでつながっているオブジェクトは同時にすべて解放しない限り自動で解放していないのではないか?と疑問に思ってしまいます。
そもそも・・・
そもそも、CoreDataはソースが見れません。appleの出しているライブラリなのでこれは仕方ないとも言えます。 しかし、中身の見えないブラックボックスにデータの保存という重要な役割を任せていいのか?というのが非常に気になってきました。 オープンソースであれば何かしらエラーが起きた時に原因もしっかりわかるし、対策も立てられます。 しかし、ソースが見れないと原因はわからないし、対策も立てにくくなります。 そもそも、自分の使ってる使い方がそのライブラリの正しい使い方なのかどうかもわかりません。 一番最初に上げたCoreDataの認識
- 簡単・お手軽
- データ保護など、安全にデータを保存してくれている
- メモリ関係も自動でやってくれる
のうち簡単・お手軽以外の利点が怪しくなってきた今となっては、SQLiteで新しく簡単・お手軽なライブラリをつくろうかなと思ってます。
というわけで、自分がCoreDataからSQLiteに逆戻りしそうな理由たちでした! 追記: SQliteで簡単・お手軽なライブラリ作って公開しています TSSqlite