#! /usr/local/bin/macruby # coding:utf-8 # framework 'Foundation' framework 'CoreData' DB_NAME = "Question" ENTITY_NAME = "Question" class DB def managedObjectModel if (@managedObjectModel != nil) return @managedObjectModel end modelURL = NSURL.URLWithString Dir.pwd+"/#{DB_NAME}.momd" @managedObjectModel = NSManagedObjectModel.alloc.initWithContentsOfURL(modelURL) return @managedObjectModel end def persistentStoreCoordinator if (@persistentStoreCoordinator != nil) return @persistentStoreCoordinator end storeURL = NSURL.fileURLWithPath Dir.pwd+"/#{DB_NAME}.sqlite" error = Pointer.new(:object) @persistentStoreCoordinator = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(self.managedObjectModel) if (!@persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:storeURL, options:nil, error:error)) puts "Unresolved error #{error}, #{error.userInfo}" abort() end return @persistentStoreCoordinator end def managedObjectContext if (@managedObjectContext != nil) return @managedObjectContext end coordinator = self.persistentStoreCoordinator if (coordinator != nil) @managedObjectContext = NSManagedObjectContext.alloc.init @managedObjectContext.setPersistentStoreCoordinator(coordinator) end return @managedObjectContext end def saveContext error = Pointer.new(:object) managedObjectContext = self.managedObjectContext if (managedObjectContext != nil) if (managedObjectContext.hasChanges || !managedObjectContext.save(error)) puts "Unresolved error #{error}, #{error.userInfo}" abort() end end end end `/Developer/usr/bin/momc #{DB_NAME}.xcdatamodeld #{DB_NAME}.momd` if File.exist? "#{DB_NAME}.sqlite" File.delete "#{DB_NAME}.sqlite" end db = DB.new p model = db.managedObjectModel var = {"YEAR" => 2010} request = model.fetchRequestFromTemplateWithName("QuestionsWithYear", substitutionVariables:var) p request.predicate.predicateFormat db.saveContext
2011年8月17日水曜日
MacRuby から CoreData
アプリに入れるデータは MacRuby で作っていたりします。Objective-C でも良いんだけど、コンパイルとか面倒だし、テキスト処理は Ruby だと楽チンだし♪
Xcode4 の Model Editor
Xcode には CoreData で操作するモデル作成する GUI エディタがある。こんなやつ。
画像の左に "Fetch Requests" という項目があるが、これは名前が示す通り NSFetchRequest を定義しておけるものだ。ソースコードに書くといろいろややこしく、見づらくなってしまうけど、モデルデータに格納しておけば比較的すっきりと書くことができる。
NSURL *modelURL = [NSURL URLWithString:path]; NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; // モデルデータにある Fetch Request の一覧を取得 NSDictionary *requests = [managedObjectModel fetchRequestTemplatesByName]; // 名前を元に特定の Fetch Request を取得 NSFetchRequest *request = [managedObjectModel fetchRequestTemplateForName:@"MyFetchRequest"];さて、この Fetch Request であるが、"year == 2011" などの定数を元に作成してもあまり恩恵はない。やはり変数を扱える方が嬉しい。Xcode3 までは Editor のポップアップに VARIABLE という項目があり、簡単に利用する事ができた。Xcode4 からはこの選択項目がなくなり、自分で記入しなければいけないので注意が必要。NSPredicate を使った事がある人なら分かると思うが、変数を扱うには $ 記号を使って変数名を指定してあげれば良い。この時、expression を選択しておくのがポイントだ。 このようにして作成した Fetch Request のテンプレートは fetchRequestTemplateForName: ではなく fetchRequestFromTemplateWithName:substitutionVariables: を使って呼び出す。
NSDictionary *variables = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:2010] forKey:@"YEAR"]; NSFetchRequest *request = [managedObjectModel fetchRequestFromTemplateWithName:@"QuestionsWithYear" substitutionVariables:variables];
2011年8月16日火曜日
CoreData をちょっと説明する
概要
iOS 上でいくらか面倒な情報を保存、読み出ししなければ行けないような場合、NSUserDefaults や NSKeyedArchiver では直ぐに限界を感じてしまう。ファイルへの読み書きは速度が遅いし、何より特定の項目を検索できないのが辛い。そのような時のために、iOS では CoreData というデータベースを扱えるようになっている。CoreData の大元
ファイル
CoreData はそのモデルの情報をファイルに格納している。 Xcode 上では .xcdatamodeld というフォーマットで、これは Xcode 上で使うための専用のフォーマットで、iPhone 側に渡す事はない。iPhone 上ではこれをコンパイルして .momd というフォーマットにして渡す。.momd はビルド時に作成されるが、自分で作りたい時は以下のようにしてファイルを作成する。/Developer/usr/bin/momc Model.xcdatamodeld Model.momdこの .momd から .sqlite などの Store が作成されるので、大元の .xcdatamodeld に変更を加えなくて済むように事前によく設計した方が良い。簡単な変更ならバージョン管理機能でほとんどコードをいじらずにデータベースをバージョンアップできる。
クラス
- NSManagedObjectModel
- 上記のモデルデータを扱うためのクラス。
- NSPersistentStoreCoordinate
- Store つまりDB本体を扱うためのクラス。NSManagedObjectModel をもとに DB を作成する。
- NSManagedObjectContext
- メモリ上のDBという感じのクラス。データの変更などはここに蓄えられ、アプリの終了時などにまとめて保存する。 データベースとのやり取りにおいて幾層にも抽象化されていて実態が分かりづらいけど、面倒な事はみんな CoreData がやってくれるのでコードに専念できる。
2011年8月9日火曜日
テストデータの利用
テストデータを利用するために Bundle に含めるのだけど、[NSBundle mainBundle] だとテスト用の Bundle ではなくてアプリの方の Bundle を参照するのでよろしくない。
こういう時は、[NSBundle bundleForClass:[MyAppTests class]] などして Bundle を指定してあげるのが良さそうだ。
NSBundle *bundle = [NSBundle bundleForClass:[MyAppTests class]]; //Accurateness test NSString *path = [bundle pathForResource:@"test_data" ofType:@"txt"]; NSString *str = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL]; STAssertTrue([MyApp myMethod:str], @"myMethod");
登録:
投稿 (Atom)