Cocoa Break Logo Top Soft Develop  脱力空間 Logo
Apple Web Badge
made by mi

概要 翻訳 ソース リンク がらくた

概要 Examples ADC Samples 3rd Parties etc CBOriginals

DotViewUndo

以下は、Mac OS X 10.5.5 上の Xcode 3.1 で説明しています(2.5 以前からの変更点も注記しています。)。

DotViewUndo は、DotView に取り消し機能を追加したものです。サンプル自体は独立していますが、説明は重複を避けるため、すでに DotView で説明したことは省いています。まず DotView を読んでから、この説明を見てください。

目次:
1 README.rtf の日本語訳
2 アプリケーションなど
3 DotView クラス
3.1 ヘッダ
3.2 ビュ−の初期化と解放と描画
3.3 追加されたアクセサメソッド
4 まとめ
5 更新履歴

1 README.rtf の日本語訳

DotView のこのバージョンは、単純で、MVC パターンにもとづかないプログラムにおいて、取り消し・やり直し機能を提供するために、どのように NSUndoManager を使うことができるかを実演する手段として、以下のものを追加します。

これは、第一に DotView のプロパティに対して分離されたアクセサ(取得と設定メソッド)を作成し、既存のメソッドを(適切な時に)これらのアクセサを呼び出すように変更し、最後にこれらのアクセサに取り消しを追加することで、行われます。

「取り消しの追加」は非常に単純です。取り消し可能であったほうがいい何らかの状態が変更されるときはいつでも、単に適切な(この場合、ウインドウごとの)取り消しマネージャーに対して、その状態変更を取り消すためにどのような呼び出しを行えばいいかを知らせます。この呼び出しは、しばしば、以前の値によって呼び出される同じメソッドです。これによって、やり直しも同様に自動化されます。

行う呼び出しが何かを取り消しマネージャーに知らせるには、以下のメソッドを使うことができます。

- (void)registerUndoWithTarget:(id)target selector:(SEL)selector object:(id)anObject;

これは、単一のオブジェクト変数をもつ単純なコールバックの登録を可能にします。さらに洗練されたコールバックに対しては、以下のメソッドを使うことができます。

- (id)prepareWithInvocationTarget:(id)target;

これは、後で再現するための、メソッド呼び出しの「フリーズドライ」を可能にします。DotView は、この両方のメソッドの使い方を実演します。

プロパティの「基本的な」値を操作し、取り消し、または再表示が起ってほしくないので、初期化や割り当て解除時にはアクセサを通過しません。

DotView が非常に単純なアプリケーションであることを強調しておくことは重要でしょう。ここでは、1 つのクラス(DotView)がモデル・ビュー・コントローラー(MVC)のすべての役割をにないます。

NSUndoManager の直接使用は、Core Data にもとづくアプリケーションでは不必要なものになるでしょう。Core Data は、オブジェクトの存続期間管理を助け、これには、取り消し・やり直しが含まれます。このような場合、キー値コーディング(key value coding, KVC)準拠メソッドは、自動的に取り消し・やり直しのサポートを持つことになります。(このような場合でも、さらなるカスタマイズのために、NSUndoManager を使うことができます。)

2 アプリケーションなど

アプリケーションの機能、ファイル構成、MainMenu.nib などは、DotView と同様なので、そちらの説明を見てください。MainMenu.nib には変更があります。以前は、スライダーとカラーウェルは、それぞれ、setRadius: setColor: というメソッドに接続されていましたが、アクセサをきちんとしたことにより、これらは、changeSize:changeColor: というアクションメソッドに変更されていて、そのなかから設定メソッドが呼ばれています。

3 DotView クラス

3.1 ヘッダ

インスタンス変数は、DotView と変わりません。メソッド宣言に以下のメソッドが追加されています。

DotView.h > インスタンスメソッド追加分
// ドットの属性に対するアクセサ - (void)setCenter:(NSPoint)newCenter; - (NSPoint)center; - (void)setRadius:(CGFloat)newRadius; // Xcode 2.5 では(float)newRadius - (CGFloat)radius; // Xcode 2.5 では (float) - (void)setColor:(NSColor *)newColor; - (NSColor *)color;

3.2 ビュ−の初期化と解放と描画

初期化と解放メソッドも、描画関係のメソッドも、DotView と同じです。

3.3 追加されたアクセサメソッド

まず アクセサメソッドのうち、取得メソッドは、基本的に値を返すだけです。color だけが少し違います。

DotView.m > color
- (NSColor *)color { return [[color retain] autorelease]; }

現在の保持 (retain) 状態を維持するため、一度 retain してから、autorelease されています。これについては、『Cocoa のためのメモリ管理プログラミングガイド』>「アクセサメソッド」を見てください。

次に設定メソッドを見てみましょう。まず、上の README.rtf で述べられていた最初の方法を使っている setColor: です。

DotView.m > setColor:
- (void)setColor:(NSColor *)newColor { if (newColor != color) { // 取り消し [[[self window] undoManager] registerUndoWithTarget:self selector:@selector(setColor:) object:color]; // 1 取り消しマネージャーに登録 // 色の更新 [color release]; color = [newColor retain]; // ビューを描画が必要なものとしてマーク [self setNeedsDisplay:YES]; } }

DotView のものとは微妙に違っています(autoreleaserelease か)が、基本的な流れは同じです。違っているのは、 1 の部分です。まず、[[self window] undoManager] でウインドウごとの取り消しマネージャーを取得しています。それに対して、取り消しする場合、このビューに対して現在の色で setColor: を呼び出すように、registerUndoWithTarget: selector: object: を使って登録しています。取り消しサポートに必要なのは、たったこれだけです。

残りの 2 つの設定メソッドは、README.rtf の説明のうち後の方のやり方を使っています。

DotView.m > setCenter:
- (void)setCenter:(NSPoint)newCenter { if (!NSEqualPoints(center, newCenter)) { // 取り消し [[[[self window] undoManager] prepareWithInvocationTarget:self] setCenter:center]; // 1 取り消しマネージャーに登録 // イスンタンス変数の更新 center = newCenter; // ビューを描画が必要なものとしてマーク [self setNeedsDisplay:YES]; } }

これも違っているのは、 1 の部分です。取得したウインドウごとの取り消しマネージャーに対して、prepareWithInvocationTarget: が呼び出されます。次が違和感を感じるところですが、取り消しの際に呼ばれるメソッドを、「取り消しマネージャー」(prepareWithInvocationTarget: の戻り値は取り消しマネージャー自身)に対して送っています。prepareWithInvocationTarget: は次に送られたメッセージを記録し、取り消しの際には、それを引数のターゲットに対して送ります。これで、取り消しのときには、変更する前の center 値で setCenter: が呼ばれることになります。ここで、メッセージが送られているものの、これは実行されません。レシーバーが違うので当然のことですが、慣れないと、このメソッドが再び呼ばれて無限ループになるような気がしてしまうかもしれません。

もうひとつの setRadius: もほとんど同じです。それぞれの設定メソッドに対して、たった 1 行で取り消しのサポートが行えました。

2.5 のサンプルでは、radiusfloat 型でした。そのため、setRadius:radiuschangeSize: メソッドが、古いサンプルから変更されています。

4 まとめ

単純なアプリケーションにおいて、取り消しを実装するのがどれほど簡単かがわかったと思います。

ただし、もっと複雑なアプリケーションでは、このようにアクセサだけによる取り消しは難しくなってきます。また、取り消しの際に、どこまでを 1 つの作業としてまとめて取り消すのかということも考えなくてはなりません。考えてみればわかりますが、ある操作が複数のデータ値を変更する場合、このサンプルのようなやり方だとうまく機能しません。これは取り消しスタックという考え方で、複数の登録を 1 つにまとめることができるようになっています。

取り消しについては、ガイドの『取り消しアーキテクチャ」を読んでください。

5 更新履歴

2007.11.13

v10.4、Xcode 2.5 で解説作成(ずっと以前の自分用のメモを修正)。

2008.09.22

v10.5.5、Xcode 3.1 用に解説更新。


管理人:神吉 秀典 E-mail:puer@ba.wakwak.com