CoreData Migration ~ Lightweight Migration を使った方法
「CoreData Migration - xcmappingmodel 編」と「CocoaBindingを使って画像管理ソフトを作成」の合わせ技です。
なお、「CoreData Migration - xcmappingmodel 編」はサンプルコードすらない旧聞の状態なので、新しいのでも問題ないという人はこちら推奨。
ちなみに私も勉強中なので、間違ってる事書いてたら突っ込みよろしく。日本語で書かれたのがホントに無いんだよね、何故か・・・
CoreDataを使っている場合EntityやPropertyなどのModelを変更する場合が、しょっちゅうあります。今回はそのModelを変更した際の対応方法。
今回は、前回「CocoaBindingを使って画像管理ソフトを作成」で作成したソフトを改良するという形で話を進めて行きます。
前回の失念点
まず、前回のアプリで「[appdelegate addImage:img forBook:b];」となってて動く訳ネェです。はい、ごめんなさい。appdelegateはそのままの意味でして、Interface builderでapplication delegate に接続しています。
addImage:forBook: は下記のようなメソッドです。
- (void) addImage:(NSImage*)image forBook:(Books*)object { Images* img = [[Images alloc] initWithEntity:[[self.managedObjectModel entitiesByName] objectForKey:@"Images"] insertIntoManagedObjectContext:self.managedObjectContext]; img.image = [image TIFFRepresentation]; img.book = object; [object addImagesObject:img]; [img release]; }
改良点
まずwindow部分でNSCollectionView を、ImageKitのIKImageBrowserViewを使います。BindingはNSCollectionViewと同じ感じにします。
ソースコード
さて、IKImageBrowserViewは渡されたオブジェクトに imageUID メッセージを送り、その返り値を元に画像をキャッシュし、パフォーマンスを維持します。
ですので、Images.arrangeObjects で得たimageに imageUID メッセージを送っていることになりますので、Categoryで無理矢理実装してやりましょう。
Images+IKImageBrowserItem.h
#import <Foundation/Foundation.h> #import <Quartz/Quartz.h> #import "Images.h" @interface Images (Images_IKImageBrowserItem) - (NSString *) imageRepresentationType; - (id) imageRepresentation; - (NSString *) imageUID; @end
Images+IKImageBrowserItem.m
#import "Images+IKImageBrowserItem.h" @implementation Images (Images_IKImageBrowserItem) - (NSString *)imageUID { return @"N/A"; } - (NSString *)imageRepresentationType { return IKImageBrowserNSDataRepresentationType; } - (id)imageRepresentation { return self.image; } - (NSString *)imageTitle { return @"N/A"; } @end
さて、これで画像は下の画像のように表示されるはずです。
Model の変更
上の画像では画像はそれぞれ全部違いますが、実は上の実装をすると、imageUID が全部同じ値を返すので、全部同じ画像になります。
ですので、Images が個別のPropertyを持つようにしてやれば良いです。ついでに、中身はNSData型なのにimageとproperty名がなっている所を、dataと変更する事にします。
まず、.xcdatamodeldファイルを選択し、メニューの「設計」→「データモデル」→「モデルバージョンを追加」でファイルを複製します。そして、上で書いた変更をします。(下の図を参考にして下さい)
Mapping
さて、モデルの変更が終われば、次は変更箇所をマッピングをするためのファイルを作ります。
メニューの「ファイル」→「新規作成」→「Resource」→「Mapping Model」→任意のファイル名を入力します。
次に、変更する前とした後のモデルを選択するように求められてきます。*1
「ソースモデルを設定」には変更前のモデル、「デスティネーションモデルを設定」には変更後のモデルを選択して下さい。
Migration
これで移行のマッピングとモデルは出来上がりました。後はソースコードでいつするかを書くだけです。するタイミングは、Storeファイルを読み込む直前が現実的に良いです。
必要な機能としては、移行する機能、移行しているか確認する機能、の二つがあります。
移行しているか確認するメソッド
- (BOOL) isMigrateToPersistentStore:(NSPersistentStoreCoordinator*)psc toURL:(NSURL*)sourceStoreURL { NSString *sourceStoreType = NSSQLiteStoreType; NSError *error = nil; NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:sourceStoreType URL:[NSURL URLWithString:sourceStoreURL] error:&error]; if (sourceMetadata == nil) { // deal with error NSAssert(NO, @"sourceStore is none"); return NO; } NSString *configuration = nil;/* name of configuration, or nil */ ; NSManagedObjectModel *destinationModel = [psc managedObjectModel]; BOOL pscCompatibile = [destinationModel isConfiguration:configuration compatibleWithStoreMetadata:sourceMetadata]; return pscCompatibile; }
ちなみにこれは完璧と言える状態ではありません、ので説明できません*2 :-p
移行メソッド
Light-Weightなので、10.6,iPhoneOS 3.0 以降で無いと動きません。1.5だと・・・勉強しておきます。
- (BOOL) migration:(NSURL*)storeurl persist:(NSPersistentStoreCoordinator*)persistentStore { NSError *error; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; NSPersistentStore* store = [persistentStore addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeurl options:options error:&error]; if (error) { NSAssert(NO, @"addPersistentStore is nil"); NSLog(@"error: %@", error); return NO; } return YES; }
Storeをアップデート。
アップデート部分はこんな感じ。どこに書くかはわかりますよね?:-)
NSPersistentStoreCoordinator *persistentStoreCoordinator = <# NSPersistentStoreCoordinator #>; NSURL *url = <# NSURL: The storedata file url #>; // update if (![self isMigrateToPersistentStore:persistentStoreCoordinator toURL:url]) { if ([self migration:url persist:persistentStoreCoordinator] == NO) { [persistentStoreCoordinator release]; persistentStoreCoordinator = nil; return nil; } [persistentStoreCoordinator release], persistentStoreCoordinator = nil; // 使用済みなので一回破棄 persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: mom]; }
ManagedObjectModel
さてさて、これでは何故かManagedObjectModelがModelファイルを読み込まなくなりました。というのも、modelファイルが複数になったので、Modelディレクトリーが出来たため(な気配。ちょっと自身ない)。ですので、modelディレクトリーを読み込ませれば良いわけです。ちなみに、Modelディレクトリーは.momdという拡張子が付いたディレクトリーです。
- (NSManagedObjectModel *)managedObjectModel { if (managedObjectModel) return managedObjectModel; NSString *managedDataModelPath = [[NSBundle mainBundle] pathForResource:@"ImagePending_DataModel" ofType:@"momd"]; managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL: [NSURL URLWithString:managedDataModelPath]]; // managedObjectModel = [[NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles] // forStoreMetadata:nil] // retain]; return managedObjectModel; }
仕上げ
さて話を上の方に戻しまして、imageUID を合理的に動くようにしましょう。
Images+IKImageBrowserItem.m // 修正後
#import "Images+IKImageBrowserItem.h" @implementation Images (Images_IKImageBrowserItem) + (NSString *)stringWithUUID { CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault); NSString * str = (NSString*)CFMakeCollectable(CFUUIDCreateString( kCFAllocatorDefault, uuid)); CFRelease(uuid); return [str autorelease]; } - (NSString *)imageUID { if (self.UniqID) return self.UniqID; self.UniqID = [Images stringWithUUID]; return self.UniqID; } - (NSString *)imageRepresentationType { return IKImageBrowserNSDataRepresentationType; } - (id)imageRepresentation { return self.image; } - (NSString *)imageTitle { return @"N/A"; } @end
ビルドしてエラー箇所を修正したら、後は動かすだけです。
登録しているが増量が多いほど時間がかかりますが、気長に待つしか無いです。
終了すれば、上手く動く・・・はず。ちなみに私の事なので、今回も何処か抜けているかと思います。上手く動かない時はコメントに突っ込みよろしくです。
今でもよくわからん所
- Versioning. どう使うの?
- 10.5の頃とどう変わったの?
- NSMigrationManager,NSEntityMigrationPolicy の存在