Month of 4月, 2010

Core Dataのマイグレーション(手動編)

in

既存のマッピングで対応できないような場合、NSEntityMigrationPolicyを拡張してカスタマイズできることはわかったのですがドキュメントを読むだけではいまひとつピンとこなかったので実際にやってみました。トライしたのは下記のアドレス帳モデルの変換。とりあえず、v1のモデルで固定・携帯の2つの電話を持つ桃太郎、携帯電話のみを持つ浦島太郎、電話は持たない一寸法師のアドレス帳データを作成しこれをv2モデルに変換します。マイグレーションの骨子は、

  1. 電話を持たない人はそのまま新しいAddressBookエンティティにマッピング
  2. 電話を持つ人はPhone1/Phone2をPhoneBookエンティティにマッピング

の2点。1に関しては通常のマッピングで対応し、2に関してマイグレーションポリシーを拡張します。

まず、v1のAddressBookエンティティにあるphone1とphone2をPhoneBookエンティティに移行するため新しいマッピングを適当な名前で作成します。ここではAddressBookToPhoneBookという名前のマッピングモデルを作成しました。ソースにはAddressBook、デスティネーションにはPhoneBookを指定します。また、電話を持たないAddressBookインスタンスはPhoneBookへのマッピングは不要なのでフィルタ述語に

Core Dataの自動マイグレーション

in

Core Dataの使い始めの頃、マイグレーションには散々苦労したのですがいつの間にか自動マイグレーション機能なんかができていてこれがいたって簡単でした。手順としては

  1. 新しいモデルの作成
  2. マッピングモデルの作成
  3. 自動マイグレーションの指定

の3ステップだけ。

まず新しいモデルを作成する場合ですが、既存のモデルエディタに直接修正を加えるのではなくて、[設計]-[データモデル]-[モデルバージョンを追加]で新しいモデルを作成します。これで、既存のモデルをベースに新しいモデルが作成されますので新しいモデルに必要な修正を加えます。モデルの作成が終了したら[設計]-[データモデル]-[現在のバージョンを設定]で、新しく作成したモデルをカレントモデルに設定します。これが第一段階。

次はマッピングモデルの作成。ある意味、これがマイグレーションの本体となります。[新規ファイル追加]-[リソース]-[マッピングモデル]で新しいマッピングモデルを作成しますが、ここで「ソースモデル」と「デスティネーションモデル」を指定します。「ソースモデル」が既存のモデル、「デスティネーションモデル」が先に作った新しいモデルファイルになります。

このマッピングモデルに(旧)エンティティ(のどのプロパティ)が(新)エンティティ(のプロパティ)にマッピングされるかを記述していくわけです。作成直後の状態では、名前を元にマッピングが作成されているのでこれをベースに必要なマッピングを記述していけばホント簡単にマイグレーションできちゃいます。マッピングは新旧エンティティが一対一にならなくとも良くて、いくつもマッピングを作成することができます。例えばPredicateを指定すれば、ある(旧)エンティティAのうち、条件xに合致するデータは(新)エンティティA'にマッピング、条件yに合致するデータは(新)エンティティA''にマッピングなどということができるわけです。

最後は、コーディネーターに自動でやってねってお願いするのを忘れずに。

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
    [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
                            configuration:nil 
                            URL:url
                            options:options
                            error:&err]);

たったこれだけで、エンティティとその属性(もちろん関連も)移行してしかも殆どノーコード。モデルのバリデーション(例えば新しく追加した属性がオプショナルでない場合など)に注意すれば実にスマートな移行ができます。実際のところ、単なる属性追加だけとか非オプションをオプションに変えるだけとかだったらマッピングモデルの作成すら省略することができるなんてサービスしすぎな気がしなくもないです。

さらにはNSEntityMigrationPolicyをオーバーライドしてさらに複雑なマイグレーションのための手段もあるので次回はもう少し複雑なパターンを書いてみようと思ってます。

あっ、v10.4では...ちょっとね、ダメみたいです。悲しいことに。

(参考)Introduction to Core Data Model Versioning and Data Migration Programming Guide

NSTaskは~/Library/Apprication\ SupportでもOK

in

NSTaskでスクリプトを実行する時、${HOME}/Libray/Apprication\ Supportでも実行できるか不安だったので念のため試してみたところちゃんと実行できました。いや、単にパス名にスペースが含まれているから実行できるか不安だっただけですが...(MacOSXには"Program\ Files"的なものはないかと思っていましたがあるんですね。)

- (void)setCurrentDirectoryPath:(NSString *)pathで移動してやればOK。Cocoaはやっぱり! - Perlスクリプトを実行するに詳しいです。これまでtemporaryディレクトリなんかを使ってやっていたんですが、ここを使えば競合なんかの心配が限定できるのでじゃかじゃかやれます。Core Dataのデフォルト保存パスがここになっていることでようやくこのテに気がつきました。日本語を含むディレクトリでも正しく起動できました。ま、カレントディレクトリを変えちゃうんだから当たり前なんだけど、Winじゃ"Program\ Files"とか"My\ Document"とか痛いことあるし...

XCodeで外部エディタが起動できないとファイルが削除されたことになる件

XCode上でLocalizable.stringsを編集しようとすると、「ファイルは移動または削除されました」と言われて編集できないという事態が昨日突如発生して困っていたのですが、ようやく解決しました。

なんのことはない、Localizable.stringsの編集に指定していた外部エディタ(Smultron)を削除してしまっていたためでした。開発のストップしてしまったSmltronの代わりにFraiseをインストールした際、Smultronは削除したのですが、指定していた外部エディタが起動できないとXCodeは「ファイルがないぜ!」と言うようです。デフォルト状態戻したらちゃんと編集できるようになりました。エディタがないからってファイルが削除されたかのメッセージはどうかな?Finder上ではちゃんと存在するのに。

こんなメッセージだとXCodeが壊れたのかと思ってしまいます。もう少しで再インストールするとこでした。なんでLocalizable.stringsをなんで外部エディタ編集にしていたかっていうと、ATOKがXCodeで使えないって問題しかたなくそうしていたわけですが意外なところで足下救われました。まったくくだらないところで時間を使いました。(;_;)

しかし、ちょうど良いタイミングでATOKのほうもパッチがでたみたいだしとりあえず良かった。徒労感満々ですが....

ガベージコレクションに気をつけて

in

先日作成したftpのサンプルアプリですがNSInputStream/NSOutputStreamのretain/releaseを書いてないのでGCオプションをなしにするとエラーとなります。自分ではこのオプションを変更した記憶はないのですが、なにせv10.4対応アプリを作成中なので色々いじっているうちに、なぜかDebugバージョンは[GCC_ENABLE_OBJC_GC, -fobjc-gc-only]で、Releaseバージョンでは非対応になってしまったようです。せめて逆なら良かったのですが、こういうオプションは構成毎に連動してくれればいいのに。と思ったりもしますがもちろんそれくらい自分で管理しないとダメ。とにかく、これにハマると、Bad AccessやらSelectorがないよ!とかいろんなエラーが出て、迷いに迷うので要注意です。

Cocoaでアプリケーション名をローカライズするには...

in

Cocoaでリソース関連をローカライズするにはIBで日本語なら日本語のリソースを作ってやればいいのだけれどアプリケーションの名称はこれではローカライズできません。

スクリーンショット