ページ

2010年1月28日木曜日

BlogAssistant(10) CoreData - 複数のプロセスから CoreDataを扱う 〜 NSManagedObjectのリロード

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

Cocoaの日々: BlogAssistant(9) CoreData - awakeFromInsert の使用

BlogAssistant ではSafariプラグインを使ってページをクリップした後、別の専用アプリケーションの方でそれを表示させる。データの受け渡しには CoreData を使う。こんなイメージ。




しかし、CoreData は複数のプロセスから使う様にはできていない。Apple公式のドキュメントには複数プロセスが可能であるとの記載はないし、APIや説明を読む限り複数プロセスから扱うように出来ているとは思えない(ただしマルチスレッドは OK。注意点は色々あるものの、ドキュメントにも説明が割かれている)。

Safariプラグインと、ビューアを分けてしまったものの困ったことになった。このまま無理矢理自己責任で進むか、それとも当初のように Safariプロセス内で全部やるか。うーむ。


とりあえずできるところまで別プロセスでやってみることする。今回は用途はクリティカルではないので最悪DBが壊れても困らない。


さて、その上で最初に解決しなければならないのは、Safariプラグインでクリップした時に書き出したCoreDataの変更を、別プロセスの表示に反映する方法を見つけること。

トリガーは、プラグイン側で書き出し後にビューアアプリをキックするのでいいとして、問題はその後。複数プロセスからの利用を想定していない CoreDataスタックは SQLiteが書き変わったことが検出できないのはもちろんのこと、一発で NSManagedObjectContext にデータを再読み込みさせる方法がない(見つけられなかった)。

NSManagedObjectContext には -[reset] が用意されているが、既にNSManagedObjectContext内にフェッチされていた内容がすべて消えてしまう。再フェッチは自分でやらなければいけないが NSArrayController と連携している場合にやり方がよくわからない。

※探してみると Stack Overflow に同じ目的の質問が出ていた。それに対する答えは、複数プロセスで直接データへアクセスするの無理だろう、片側のプロセスで一括して管理するようにした方がいい(クライアント-サーバモデルを取る)とのこと。
NSManagedObjectContext and NSArrayController reset/refresh problem - Stack Overflow

改めて複数プロセスからのアクセスは無謀であることがわかったが。。


いろいろ考えて思いついたのは NSManagedObjectContext を再作成すること。つまり今まで使っていたものを解放して、同じ NSPersistentCoordinator から新たに NSManagedObjectContext を作成する。そしてそれを NSArrayController へ再設定してやる。

実際のコードはこんな感じ。

CoreDataManager.m

- (NSManagedObjectContext*)recreateContext
{
if (managedObjectContext) {
[managedObjectContext release];
managedObjectContext = nil;
}
return [self managedObjectContext];
}


表示を最新に更新する場合はこれを呼び出して、戻り値で取得した新しい NSManagedObjectContext をビューが参照している NSArrayController へ設定してやれば良い。

コード例:

-(IBAction)reset:(id)sender
{
[arrayController setManagedObjectContext:
[[CoreDataManager sharedManager] recreateContext]];
}



実際に試したところうまく動いた(更新された)。


- - - -
やっておいて何だが、少々荒技でかなりの無駄遣いな方法だと思う。特に件数が多い場合は NSManagedObjectContextを作り直すたびに全件読み込みになるので時間とメモリを使う。メモリは気になるので後で Instruments で調べてみたい。