このページは「iPhoneプログラミング入門」(工学社刊、ISBN 978-4-7775-1541-7)を読んで、もっと先へ進んでみたいと思った人への説明です。
次へ->
<- 前へ
「次の一歩」一覧へ
ビュー・コントローラ間でのデータの受け渡し、その2「元のページへデータを返す」
---(3)「デリゲート」を作るには---
実は「デリゲート」というしくみはナイ
「デリゲート」は「役割の総称」
iPhoneアプリのプロジェクトには必ず出てくる「delegate(デリゲート)」という言葉ですが、実は、「Delegate」というクラスとか、キーワードといったものは、Objective-Cには「(直接には)ありません」。これは、役割を表す言葉です。
「プロトコル」というしくみを使う
「デリゲート」の役割を実現するためには、「プロトコル」と呼ばれるしくみを用います。
そして、いろいろなプログラムで「プロトコル」を用いる目的のかなり多くが、「デリゲート」の実現なのです。逆に、「デリゲート」と出て来たら「文法はプロトコルだな」と決めてかかって10中8,9大丈夫です。
「デリゲート」に使われる「プロトコル」のしくみ
主にメソッド名に関する約束事
「プロトコル」の意味は「約束事」です。
それは、主に「メソッド名」に関する約束事です。
それは、「このような名前のメソッドを、わたしとアナタで共通に使うようにすれば、お互いのデータ交換が簡単にできますヨ」という約束事です。
そのようなメソッドを複数まとめたものが「プロトコル」です。
デリゲートなプロトコルは、使い方で決まる
「デリゲート」な役割を実現させるためには、文法であるプロトコルを用いて、ある決まった使い方をします。
それは、ひとつのクラスが、「わたしを利用したいクラスは、このメソッド名を使ってください」と宣言する使い方です。
なぜクラス中に定義しないのか
そこで、そうしたプロトコルには、「使ってほしがっているクラス」に「Delegate」という名前を付加したプロトコル名をつけるのが普通です。使って欲しがっているのが「MyClass」という名前のクラスなら「MyClassDelegate」というプロトコルになります。
だったら、なぜそのクラス「MyClass」中にそういうメソッドの定義をしないのか、という疑問が沸いて来ますね。
それは、「使ってください」だからです。世の中、所有者と使用者が別だと、権利関係が何かと複雑になるじゃありませんか。ですから、思い切って両者とも「所有」しないことにしてあるのです。
ただ、「どっちがどっちに使ってくれと言ったんだっけ」ということを忘れないために、「使ってくれ」と言っているほうの名前をつけておくのです。
ここから先は実際にコードを書きつつ、プロトコルのしくみとデリゲートの意味をさらに考えて行きましょう。
プロトコル「MyModalViewControllerDelegate」の概要
モーダルビューだと、親子関係がわかりやすい
これから、互いに切り替える2つの画面、正確には2つのビューコントローラ間で「共通のメソッド」を使うように約束する、プロトコルを書きます。
その際、「切り替え元」「切り替え先」と書いて行くとややこしいのですが、今回は切り替え先を「対等のビューコントローラ」ではなく「モーダルビューコントローラ」にしましたから、区別がハッキリしますね。
わたしを閉じるとき使ってください
今回作成するプロトコルは、「モーダルビューを閉じるときに何をするか」を決めるために使います。
つまり、MyModalViewControllerが、「わたしを閉じるとき、このメソッド名を使ってください」と、「親元」に伝えます。
そこで、プロトコルの名前は「MyModalViewControllerDelegate」にしましょう。
デリゲートなプロトコルは、「使ってくれと言っているクラス」の定義と「同居させる」
「プロトコル」はどのクラスも「所有」するものではありません。でも、使ってもらいたい側の情報を盛り込みます。ですから、「使ってください」と言っている側のクラスの定義と「同居させる」と便利です。
すなわち、「使ってください」と言っているクラスを定義するヘッダ・ファイルに書きます。でも、クラスの定義には含まれませんから、「クラスの定義の外」に書きます。
プロトコル「MyModalViewControllerDelegate」の作成
「MyModalViewController.h」を編集する
本プロジェクト「ShowMessage」では、「使ってください」と言い出すのは「MyModalViewController」です。そこで「MyModalViewController.h」を編集します。
今、前ページで、テキストフィールドなどを作成したことにより、記述はリスト1のようになっているはずです。みなさんそれぞれポリシーがあると思いますので、正確にこのようでなくても構いません。
リスト1 MyModalViewController.hの現在の状態(例)
#import
@interface MyModalViewController : UIViewController {
IBOutlet UITextField *msgText;
}
@property (nonatomic, retain) IBOutlet UITextField *msgText;
@end
「プロトコルの定義」の大枠
このすぐ下に書きます。書くのは、「MyModalViewControllerDelegate」という「プロトコル」の定義です。まず、大枠の定義はリスト2のようになります。
リスト2 「MyModalViewControllerDelegate」という名のプロトコルの定義
@protocol MyModalViewControllerDelegate <NSObject>
//ここにメソッドを定義
@end
<NSObject> というのが気になるかもしれません。
クラスの定義で、「プロトコルを用いる」ことを示すには、<プロトコル名> のように書く、ということを本書第18章で確認しました。
プロトコルの定義でも、他のプロトコルを用いることができます。特に他のを用いない場合、<NSObject> と書いておくのです。
プロトコルで定義するメソッド
リスト2の中に、「わたし」すなわち「MyModalViewControllerオブジェクト」を閉じるときに、「使っていただきたい」メソッド名を書いておきます。リスト3のように書いてみましょう。
リスト3 プロトコル「MyModalViewControllerDelegate」に定義するメソッド
-(void)mymodalviewcontroller:(MyModalViewController *)mymodalviewcontroller didClose:(NSString *)message;
「アプリケーション・デリゲート」の中身を思い出してみよう
上のリスト3を見て「なんだこれ、こんなの知らない」と思いましたか?
実はこれ、「アプリケーション・デリゲート」によく出てくるメソッド名と同じ格好です。
たとえば、今の「ShowMessageAppDelegate.m」(実装ファイルのほう)を開いてみてください。以下のようなメソッドがあるでしょう。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
上記の「application」の部分が、リスト3の「mymodalviewcontroller」、「didFinishWithOptions:」がリスト3の「didClose:」に相当することになります。
図16 「アプリケーション・デリゲート」に出てくるメソッドに、よく似た構造なのだ
つまりわたしたちのメソッドは...
「アプリケーション・デリゲート」で必ずお目にかかった上記のメソッドは
「application:didFinishLaunchigWithOptions:」という名前で、
「application」に、「UIAppication」クラスのオブジェクト(つまり、このアプリケーション)が入り、
それが「FinishLaunchingWithOptions」したときに、
「NSDictionary」クラスのオブジェクト「launchOptions」を用いて処理をする
という話でしたね。
そこで同じように考えます。私たちが定義するリスト3のメソッドは
「mymodalviewcontroller:didClose:」という名前で、
「mymodalviewcontroller」に、「MyMordalViewController」クラスのオブジェクト(つまり、今表示されているモーダルビューコントローラ)が入り、
それが「Close」したときに、
「NSString」クラスのオブジェクト「message」を用いて処理をする
という話にするのです。
「ヘッダファイル中の定義」は、セミコロンで終わる
さてリスト3のメソッド名を「ヘッダファイル中の定義」として書くときは、波カッコなどつけず、最後にセミコロンをつけて終わりです。最終的に、プロトコル「MyModalViewControllerDelegate」の内容はリスト4のようになります。
リスト4 プロトコル「MyModalViewControllerDelegate」の定義全文
@protocol MyModalViewControllerDelegate <NSObject>
-(void)mymodalviewcontroller:(MyModalViewController *)mymodalviewcontroller didClose:(NSString *)message;
@end
これで、「デリゲート」の役割を持つ「プロトコル」の定義は終わりました。
では、ここで定義したメソッドを、「使ってもらう側(MyModalViewController)」「使ってあげる側(ShowMessageController)」で、それぞれどのように使うかを考えて行きます。
「次の一歩」一覧に戻る