Core DataでのCopy & Paste (Property list編)

Core DataでのCopy & Paste (Property list編)

Core DataでのCopy & PasteをProperty list(Dictionary)を用いて実現する方法

プレゼンテーション


概要

Copy & Pasteの流れは、Copy操作でコピー元となるオブジェクトをNSStringやNSData等のシリアライズ可能な形式にし、NSPasteboardオブジェクトに引き渡します。Paste操作でそのデータからコピーオブジェクトを生成します。

Core Dataでは、コピーの対象となるオブジェクトはNSManagedObjectのインスタンスになります。NSManagedObjectには、Copyで用いるシリアライズ用のメソッドは用意されていないので、何らかの手段を用いて実装する必要があります。

手段として以下の方法が考えられます。

  • Property listを用いる方法
  • objectIDを用いる方法
  • Custom Classを用いる方法

ここでは、Property list(Dictionary)を用いる方法について記述しています。

利点

NSManagedObjectのPropertyをProperty list(Dictionary)に書き出すので、NSManagedObjectと無関係になり、異なるアプリケーション、異なるドキュメントで再利用可能になります。

多くの場合で、最も無難な方法です。

欠点

NSManagedObjectがRelationshipを含む場合、リレーション先のオブジェクトを特定するための何らかの手段を考える必要があります。

関係が複雑である程、特定の手段が煩雑になる事が予想されます。

チュートリアル

プロジェクトの作成

  • XCodeを起動します。
  • ファイルメニューから新規プロジェクトを選択します。
  • アシスタントのApplicationグループ内のCore Data Applicationを選択し、次へをクリックします。
  • プロジェクト名を入力し、完了をクリックします。(ここではPropertyListCopyAndPasteにしました)

モデルの作成

名前と住所を持つアドレスブック用のモデルを作成します。

  • グループとファイルのModelsフォルダー内のPropertyListCopyAndPaste_DataModel.xcdatamodelの編集画面を表示させます。
  • エンティティを追加し、名前をPersonにします。
  • 属性を追加し、名前をnameにし、タイプを文字列にします。
  • 属性を追加し、名前をaddressにし、タイプを文字列にします。

Personクラス

  • Personクラスのパース
    • Personエンティティを選択します。
    • ファイルメニューから新規ファイルを選択します。
    • アシスタントの設計グループから、管理オブジェクトクラスを選択し、次へをクリックします。
    • 適切な保存場所を指定し(通常はそのままでOK)次へボタンをクリックします。
  • 完了をクリックします。
  • Person.hの編集
    • Property list関係のヘルパーメソッド定義をPerson.hに追加します。
- (NSDictionary *)propertyList;
+ (Person *)personWithPropertyList:(NSDictionary *)propertyList managedObjectContext:(NSManagedObjectContext *)context;
  • Person.mの編集
    • Person.mに以下の処理を追加します。
-propertyList
Personのname、address PropertyをProperty list(NSDictionary)に書き出します。
- (NSDictionary *)propertyList
{
	NSMutableDictionary *propertyList = [NSMutableDictionary dictionaryWithCapacity:2];
	id object;
	object = ([self name] == nil) ? (id)[NSNull null] : [self name];
	[propertyList setValue:object forKey:@"name"];
	object = ([self address] == nil) ? (id)[NSNull null] : [self address];
	[propertyList setValue:object forKey:@"address"];
	return propertyList;
}
+personWithPropertyList:managedObjectContext:
Property listからPersonオブジェクトを生成します。
-[NSEntityDescription insertNewObjectForEntityForName:]でPersonオブジェクトを生成します。
propertyListからコピー元のname、address propertyの値を取り出し、生成したPersonオブジェクトにセットします。
+ (Person *)personWithPropertyList:(NSDictionary *)propertyList managedObjectContext:(NSManagedObjectContext *)context
{
	id object;
	Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];

	object = [propertyList valueForKey:@"name"];
	object = (object == [NSNull null]) ? nil : object;
	[person setValue:object forKey:@"name"];

	object = [propertyList valueForKey:@"address"];
	object = (object == [NSNull null]) ? nil : object;
	[person setValue:object forKey:@"address"];
	return person;
}

PropertyListCopyAndPaste_AppDelegateクラス

PropertyListCopyAndPaste_AppDelegateクラスが、メインウインドウdelegateになっているので、このクラスにcopy:、paste:メソッドを実装します。データの管理、表示はNSArrayControllerを用いて行います。

  • PropertyListCopyAndPaste_AppDelegate.hの編集
    • NSArrayControllerのメンバ変数の追加
@interface PropertyListCopyAndPaste_AppDelegate : NSObject 
{
	.
	.
	.
	IBOutlet NSArrayController *arrayController;
}
    • -copy:と-paste:メソッドの定義を追加します。
- (IBAction)copy:(id)sender;
- (IBAction)paste:(id)sender;
  • PropertyListCopyAndPaste_AppDelegate.mの編集
    • Person.hのインクルード
#import "Person.h"
NSString *PersonPBoardType = @"PersonPBoardType";
-copy:の実装
NSPasteboardで扱うデータ形式を-[NSPasteboard declareTypes:owner]で登録します。ここでは、nameの一覧をText形式でも扱える様に2通りのデータ形式を用いる事にします。
-[Person propertyList]でPersonのPropertyをProperty listに書き出し、propertyListArrayに追加します。
propertyListArrayを+[NSArchiver archivedDataWithRootObject:]でシリアライズし、データ形式をPersonPBoardTypeとしてNSPasteboardオブジェクに引き渡します。
- (IBAction)copy:(id)sender
{
	NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
	NSArray *types = [NSArray arrayWithObjects:PersonPBoardType, NSStringPboardType, nil];
	[pasteboard declareTypes:types owner:self];

	NSArray *selectedObjects = [arrayController selectedObjects];
	NSEnumerator *enumerator = [selectedObjects objectEnumerator];
	Person *person;
	NSMutableArray *propertyListArray = [NSMutableArray arrayWithCapacity:[selectedObjects count]];
	NSString *string = [NSString string];
	while(person = [enumerator nextObject]) {
		[propertyListArray addObject:[person propertyList]];
		string = [NSString stringWithFormat:@"%@%@?n", string, [person name]];
	}
	
	NSData *data = [NSArchiver archivedDataWithRootObject:propertyListArray];
	[pasteboard setData:data forType:PersonPBoardType];
	[pasteboard setString:string forType:NSStringPboardType];
}
-paste:の実装
NSPasteboardオブジェクトからPersonPBoardType形式のデータを取得し、+[NSUnarchiver unarchiveObjectWithData:]でProperty listの配列に戻します。
+[Person personWithPropertyList:managedObjectContext:]でコピーを生成し、NSArrayControllerに追加し、表示される様にします。
- (IBAction)paste:(id)sender
{
	NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
	NSString *availableType = [pasteboard availableTypeFromArray:[NSArray arrayWithObject:PersonPBoardType]];
	if ([availableType isEqualToString:PersonPBoardType]) {
		NSManagedObjectContext *context = [self managedObjectContext];
		NSData *data = [pasteboard dataForType:PersonPBoardType];
		NSArray *array = [NSUnarchiver unarchiveObjectWithData:data];
		NSEnumerator *enumerator = [array objectEnumerator];
		id propertyList;
		while(propertyList = [enumerator nextObject]) {
			[arrayController addObject:[Person personWithPropertyList:propertyList managedObjectContext:context]];
		}
	}
}

ユーザーインターフェイスの作成

  • XCodeのグループとファイルのResourcesフォルダ内のMainManu.nibをダブルクリックします。
  • XCodeでグループとファイルのPropertyListCopyAndPaste_AppDelegate.hをInterface Builder(以下IB)のMainManu.nibウインドウドラッグし、IB内のPropertyListCopyAndPaste_AppDelegateクラスのOutletとActionを更新します。
  • IBパレットからNSArrayControllerをMainManu.nibウインドウドラッグします。
  • PropertyListCopyAndPaste_AppDelegateのOutlet arrayControllerをNSArrayControllerと接続します。
  • IBパレットからNSTableViewをWindowに追加します。
  • IBパレットからNSButtonを2つ追加します。
    • 1つはNSArrayControllerのadd:アクションに接続します。
    • もう1つはNSArrayControllerのremove:アクションに接続します。
    • ボタンの名称はそれぞれAdd、Deleteとします。
  • NSArrayControllerの設定
    • NSArrayControllerを選択し、cmd+1を押します。
    • ModelのRadioButtonでEntityを選択します。
    • Object Class NameをPersonにします。
    • Automatically prepares contextをチェックします。
  • Cocoa Binding設定。
    • cmd+4でBinding設定パネルを表示させます。
    • NSTableViewの設定
      • NSTableViewを選択し、cmd+1を押します。
      • 複数選択出来る様に、Multiple Selectionをチェックします。
    • NSArrayControllerの設定
      • NSArrayControllerを選択します。
      • NSManagedObjectContextのBind toをPropertyListCopyAndPaste_AppDelegateにし、Model Key PathをmanagedObjectContextに設定します。
    • NSTableViewColumn
      • NSTableViewの左のColumn(NSTableColumn)を選択します。
      • valueのBind toをNSArrayControllerにし、Controller KeyをarrangedObjects、Model Key Pathをnameにします。
      • NSTableViewの右のColumn(NSTableColumn)を選択します。
      • valueのBind toをNSArrayControllerにし、Controller KeyをarrangedObjects、Model Key Pathをaddressにします。

テスト

  • XCodeでビルドして実行させます。
  • AddボタンでPersonオブジェクトを追加します。
  • name、addressを適当に入力します。
  • 行を選択し、Copy操作を行います。
  • Paset操作を行い、Copy & Pasteが正常に行われる事を確認します。
  • 複数行選択した場合も問題ない事を確認します。