新しいサイトはこちらです↓
Cocoaの日々
※このサイトは閉鎖せずに今後も公開しておきます。
長い間ありがとうございました。
よければ新しいサイトの方も訪問してみて下さい。
橋口
2010年6月13日日曜日
サイトをリニューアルしました
投稿者 xcatsan 時刻: 22:27
2010年6月12日土曜日
CoreData - トランザクション(5) まとめ
前回までの検証結果まとめ。
NSManagedObjectContext操作まとめ
操作 | 説明 | ディスク操作 | Undo/Redoの履歴 |
---|---|---|---|
save | 現時点でのメモリの内容を保存する | 書き出し | なし |
rollback | ディスク上の内容に戻す | (必要なら)読み込み | クリア |
undo | 1つ前の操作を取り消す(操作直前の内容に戻す) | なし | なし |
redo | 次の操作をもう一回行う(取り消した操作を復活) | なし | なし |
reset | メモリの初期化(変更の破棄、読み込まれたオブジェクトの破棄) | なし | クリア |
(参考)NSManagedObjectContext Class Reference - Undo Management
今まで検証に使ったソースは GitHub からダウンロードできる。
CoreDataTransaction at 2010-06-12 from xcatsan's SampleCode - GitHub
2010年6月11日金曜日
CoreData - トランザクション(4) reset
-[NSManagedObjectContext reset] を試す。
コードを追加し、
ボタンに紐づける。
実行。resetボタンを押すと、
データが消える。
これは NSManagedObjectContext(メモリ)上のデータが消えただけで、ディスク上のデータは残っている。
試しに reload ボタンを追加する。
ボタンが押されたら、NSArrayController を使っているので prepareContent を投げてみる。
すると reset で一旦消えた後、
reloadボタンで復活。
reset するとそれまでに読み込まれた NSManagedObject がすべて開放される(ドキュメントでは "forgotton" という表記)。当然ながら直前の変更状態はディスクには残らず破棄されてしまう。Undo/Redo履歴もクリア。用途としては NSMangedObjectContext のインスタンスを別の用途で使いまわす場合の初期化に使うことが考えられる。
2010年6月10日木曜日
CoreData - トランザクション(3) undo と redo
undo と redo をやってみる。
まずアクションメソッドを用意する。
Interface Builder を開き、Undo/Redoのボタンを追加する。これを先程のメソッドに紐づける。
実行する。確かに Undo/Redo が効いている。なお標準でメニューのUndo/Redoが FirstResponderにひもづけられており、実は今回のボタンを用意しなくてもメニューから Undo/Redoが行えた。
save と rollback の関係だが次のようになっていた。
(1) save後、Undo/Redo操作 は行える
(2) rollback後、Undo/Redo操作は行えない(操作履歴がクリアされる)
2010年6月9日水曜日
CoreData - トランザクション(2) rollback
rollback を試してみよう。前回のサンプルに Rollback ボタンを追加する。
実行してみよう。
新規にレコードを追加し、入力したところで Rollback ボタンを押す。
すると新規レコードが取り消されるのがわかる。
複数の操作も試してみよう。
まず新規レコードを追加し
元からあったレコードを修正し
1レコード削除する
そして Rollback
元に戻った。
2010年6月8日火曜日
CoreData - トランザクション(1) 準備
CoreData のトランザクションについて調べてみる。
NSManagedObjectContext では Undo Management としていくつかメソッドが用意されている。
NSManagedObjectContext Class Reference - Undo Management
これらの挙動を試す簡単なサンプルプログラムを組んでみよう。
今回はまずレコードの表示、追加、削除、変更ができるプログラムを組む。
まず Xcodeで新規プロジェクトを作る。このとき "Use Core Data for storage" にチェックを入れておく。
ビューを用意する。NSTableView を作り author と titile 列を作る。
モデルとビューの紐付けに NSArrayController を使う。
Modeを Entity とし、Entity Name に Book を指定する。これでモデルとつながった。
NSTableView内の NSTableColumnのバインディング設定を開、Array Controller を指定する。
これで MVCがつながった。
モデル(Book) ← コントローラ(ArrayController)←ビュー(NSTableView/NSTableColumn)
ただし CoreDataを扱うにはもう一手間必要で ArrayController に NSManagedObjectContext を渡す必要がある。今回 NSManagedObjectContextはコード内で生成している為、Interface Builderでは接続できない。この為、コード上で紐付けておく。
arrayController はあらかじめ IBOutlet で定義し、InterfaceBuidlerで紐付けておく。
CoreDataTransaction_AppDelegate.h
モデル(Book) ← コントローラ(ArrayController)←ビュー(NSTableView/NSTableColumn) ↓ CoreData(NSManagedObjectContext)
これでベースができた。後はデータ追加と削除のコードを付けておこう。
[+][ー]ボタンを作る。
NSArrayController には add: と remove: が用意されているので、それぞれ接続する。
これだけで新規追加と削除が行えるようになる。
さて動かしてみよう。追加、変更、削除が行える。
File メニューから Save を選択するとディスク(XML)に保存ができる。これは新規作成時のテンプレートが saveAction: を実装していて、メニューのSaveと紐づいている為。
ソース:
CoreDataTransaction at 2010-06-08 from xcatsan's SampleCode - GitHub
今日はここまで。
2010年6月7日月曜日
カテゴリでクラスメソッド定義
カテゴリはクラスメソッドもいける。
こうなら、
普通に呼び出せる。
ファクトリメソッドなどの追加に使えそうだ。
- - -
近日ブログのリニューアル予定(URLが変わります)。
2010年6月6日日曜日
今週のCocoa情報(6/6) - 今週気になった Cocoaプログラミング情報の紹介
what are alternatives to throwing exceptions in objective c - Stack Overflow
@throw よりも NSError を使う方が "the Apple recommended pattern" とのこと。確かに Cocoa Framework では例外より NSErrorを使う方が多い。
iPhone開発で便利なcocoa.vim - hellkite 日記と雑記とメモ。
MacVimとcocoa.vimで世界が変わった - Meltdown Countdown
VimでCocoa開発する話題。普段 PHPアプリなどは vimを使っているので気になる。
Cocoa nonatomic properties - Stack Overflow
プロパティ設定で "nonatomic"をつけない場合、ロック/アンロックのオーバーヘッドがかかる。
- - - -
今回も少なかった。
2010年6月5日土曜日
CoreData - Object ID(その5)情報
(前回)Cocoaの日々: CoreData - Object ID(その4)Object ID から NSManagedObject を取得する #2
NSManagedObjectID についての情報など。
NSManagedObjectID into NSData - Stack Overflow
NSURL へ変換後、NSKeyedArchiver を使い NSData へ格納。
Cocoa with Love: Safely fetching an NSManagedObject by URI
NSManagedObjectIDに関して簡潔だがわかりやすい説明。URIから NSManagedObject を取得するカテゴリを紹介している。
@implementation NSManagedObjectContext (FetchedObjectFromURI) - (NSManagedObject *)objectWithURI:(NSURL *)uri { : }
例外を出さず、確実に非faultのオブジェクトを取得する作りになっている。
Core Data Programming Guide: Using Managed Objects
Managed Object IDs and URIs
Mac Dev Center の解説。
2010年6月4日金曜日
CoreData - Object ID(その4)Object ID から NSManagedObject を取得する #2
(前回)Cocoaの日々: CoreData - Object ID(その3)Object ID から NSManagedObject を取得する
Object ID から NSManagedObject を取得するメソッドは、前回の -[objectWithID:] を含め3つある。
クラス:NSManagedObjectContext
- (NSManagedObject *)objectWithID:(NSManagedObjectID * - (NSManagedObject *)objectRegisteredForID:(NSManagedObjectID *)objectID - (NSManagedObject *)existingObjectWithID:(NSManagedObjectID *)objectID error:(NSError **)error
今回はこれらの動作を比較してみる。
1. objectWithID: の動作
(1) まず Object ID に該当するオブジェクトが、NSManagedObjectContext内に登録されているかチェックする。登録されている場合は、そのオブジェクトを返す(非Fault)。
(2) 登録されていない場合はPersistent Storeからフェッチする。
a) 該当レコードが存在する場合
フェッチしたオブジェクトを返す(Fault)
b) 該当レコードが存在しない場合
例外がスローされる。
(例) CoreDataObjectID[12483:80f] An uncaught exception was raised CoreDataObjectID[12483:80f] CoreData could not fulfill a fault for '0x226340 <x-coredata://677CA547-4D80-417A-8810-70847FB0375D/Book/p20>' CoreDataObjectID[12483:80f] *** Terminating app due to uncaught exception 'NSObjectInaccessibleException', reason: 'CoreData could not fulfill a fault for '0x226340 <x-coredata://677CA547-4D80-417A-8810-70847FB0375D/Book/p20>'' *** Call stack at first throw: ( 0 CoreFoundation 0x9253bbda __raiseError + 410 1 libobjc.A.dylib 0x97efc509 objc_exception_throw + 56 2 CoreData 0x9469a00e _PFFaultHandlerLookupRow + 174 3 CoreData 0x94699f57 -[NSFaultHandler fulfillFault:withContext:] + 39 :
2. objectRegisteredForID: の動作
NSManagedObjectContext内に登録されているかのみチェックする。
a) Object ID に該当するオブジェクトがNSManagedObjectContext内に登録されている場合
該当するオブジェクト(NSManagedObject)を返す(非Fault)。
b) 該当レコードがNSManagedObjectContext内に登録されていない場合
nil を返す。例外はスローされない。
3. existingObjectWithID:error: の動作
このメソッドは Mac OS X v10.6 から追加された(iPhone OS では 3.0以降)。基本動作は objectWithID: と同じだがヒットしなかった時は例外はスローせず、nilを返す。また errorから原因を知ることができる。
(1) まず Object ID に該当するオブジェクトが、NSManagedObjectContext内に登録されているかチェックする。登録されている場合は、そのオブジェクトを返す(非Fault)。
(2) 登録されていない場合はPersistent Storeからフェッチする。
a) 該当レコードが存在する場合
フェッチしたオブジェクトを返す(非Fault)※ここは試した時には何故か Faultにならなかった(ので、非Faultにしてある)。
b) 該当レコードが存在しない場合
nilを返す。この時、errorに原因が格納される。
(例)Error Domain=NSCocoaErrorDomain Code=133000 UserInfo=0x21e6f0 "Attempt to access an object not found in store."
サンプル:CoreDataObjectID at 2010-06-04 from xcatsan's SampleCode - GitHub
※コメントを修正することで上記3種類が試せるようになっている(かなりわかりづらいが)。
- - - -
Mac OS X v10.6 / iPhone OS 3.0 では existingObjectWithID:error: が一番使い勝手がいい。
2010年6月3日木曜日
CoreData - Object ID(その3)Object ID から NSManagedObject を取得する
Object ID の URI から NSMangedObject を取得することができる。ステップは2つ。
(1) -[NSPersistentStoreCoordinator managedObjectIDForURIRepresentation:] を使い、NSManagedObjectID を取得する (2) -[NSManagedObjectContext objectWithID:] に(1)の結果を渡し、NSManagedObject を取得する。
NSPersistentStoreCoordinator Class Reference - managedObjectIDForURIRepresentation:
NSManagedObjectContext Class Reference - objectWithID:
やてみよう。
サンプル:CoreDataObjectID at 2010-06-03 from xcatsan's SampleCode - GitHub
前回までのサンプルに次の2つの修正を加える。
1. アプリ終了時に選択していたレコードの Object ID (URI) を User Defaults へ保存する。
実行してみよう。まず行を選択し、アプリを終了する。
続いてアプリを起動し、ログを確認する。
出た。
- - - -
一連の検証で Object ID がアプリの状態保持と復帰(レジューム)に利用できそうなことがわかった。
2010年6月2日水曜日
CoreData - Object ID(その2)モデルのバージョンを変える
モデルのバージョンを変えると Object ID は変化するのか?試してみた。
バージョンを追加後(v2)、createdを追加した。
マッピングモデルを追加し、現在のバージョンを先程追加したもの(v2)に設定する。
(参考)Cocoaの日々: CoreData - マイグレーション
ビルド後に実行してみる。
(なお実行時にエラーが出る場合は、クリーニングしたのち再ビルドすると良い)。
結果は変わらず。これはまあ当然か。
1レコード追加してみる。
UUIDの部分(677CA547-...)は変わらないようだ。モデルのバージョンによって変化しないことがわかった。
SQLite DB の中身。
sqlite> select * from z_metadata;
1|677CA547-4D80-417A-8810-70847FB0375D|bplist00? _NSStoreModelVersionIdentifiers_NSPersistenceFrameworkVersion_NSStoreModelVersionHashes[NSStoreType_ NSStoreModelVersionHashesVersion__NSAutoVacuumLevel???
メタ情報に変化はないようだ。最初のカラム名に Version とついていたので、レコードが追加されると思ったがそうはならないようだ。
サンプル:
CoreDataObjectID at 2010-06-02 from xcatsan's SampleCode - GitHub
2010年6月1日火曜日
CoreData - Object ID(その1)NSManagedObjectID
NSManagedObject の Object ID
NSManagedObjectには、オブジェクトを一意に識別する為にObject ID割り振られている。この ID は NSManagedObjectID型として定義されていて、-[NSManagedObject objectID] で取得できる。
NSManagedObject Class Reference - objectID
NSManagedObjectID
このNSManagedObjectIDは次のメソッドを持っている。
- (NSEntityDescription *)entity - (BOOL)isTemporaryID - (NSPersistentStore *)persistentStore - (NSURL *)URIRepresentation
NSManagedObjectID Class Reference
saveするまでの間、Object ID は一時扱いとなる。その場合、-[isTemporaryID] が YES を返す。
-[URIRepresentation]は Object ID のURI表記を返す。
URIRepresentation の例
x-coredata://1C336A08-41AD-48E9-8A3F-8AFF17244055/Book/p1
(以下推測)
x-coredata | プロトコル
1C336A08-41.. | モデルの UUID(もしくは類似のID)
book | エンティティ名
p1 | 'p' + プライマリキー
なおsave前の(一時的な)Object ID の URIRepresentation は次のようになる。
x-coredata:///Book/t7839D4D0-A144-4C95-B14C-33AEFAF337632
リファレンスによれば、Object ID は他のアプリケーションも含めユニークなものになるとのこと。終了時に plistへ保存しておいて、次回起動時にこのIDから前回使っていた情報を取得するなんて用途に使える。
サンプル
Object ID を見るための簡単なサンプルを用意した。
ソース:CoreDataObjectID at 2010-06-01 from xcatsan's SampleCode - GitHub
モデルの定義はこんな感じ。
実行するとテーブルが現れる。Addで追加ができる。この時点ではまだテンポラリなIDが割り振られている。
saveすると正式なものが割り振られる。
この時の SQLiteの中身。
$ sqlite3 storedata SQLite version 3.6.12 Enter ".help" for instructions sqlite> .schema zbook CREATE TABLE ZBOOK ( Z_PK INTEGER PRIMARY KEY, Z_ENT INTEGER, Z_OPT INTEGER, ZTITLE VARCHAR, ZAUTHOR VARCHAR ); sqlite> select * from zbook; 1|1|1|Book1|Neko sqlite> sqlite> .schema z_metadata CREATE TABLE Z_METADATA (Z_VERSION INTEGER PRIMARY KEY, Z_UUID VARCHAR(255), Z_PLIST BLOB); sqlite> select * from z_metadata; 1|677CA547-4D80-417A-8810-70847FB0375D|bplist00? _NSStoreModelVersionIdentifiers_NSPersistenceFrameworkVersion_NSStoreModelVersionHashes[NSStoreType_ NSStoreModelVersionHashesVersion__NSAutoVacuumLevel???
ObjectID に含まれるUUIDが Z_METADATAに格納されているのがわかる。