このページは「iPhoneプログラミング入門」(工学社刊、ISBN 978-4-7775-1541-7)を読んで、もっと先へ進んでみたいと思った人への説明です。

次へ->
<- 前へ
「次の一歩」一覧へ

ビュー・コントローラ間でのデータの受け渡し、その2「元のページへデータを返す」

---(4)「プロトコル」のメソッドを「使ってもらう」側のコード ---

ヘッダファイル「MyModalViewController.h 」の編集 - 方針

「プロトコルに関係する」プロパティの追加

プロトコル「MyModalViewControllerDelegate」で名前だけ決めたメソッド「mymodalviewcontroller:didClose:」を、使ってもらうのは、MyModalViewControllerオブジェクトです。そこで、MyModalViewController自体の定義も変更する必要があります。
その編集の方針は、プロパティをひとつ増やすことです。 そのプロパティは「プロトコルに関係」するものです。データ型は、オブジェクトの「id」です。これは、「ポインタ」に相当します。

「プロトコルを使って連携する相手」と考えておこう

さて、何のオブジェクトのidなのでしょうか?「ポインタ」に相当するというのは、どういうことでしょうか?
「ポインタ」については、本書第3章第3-6節で説明してありますが、要するに「オブジェクトそのものを示す識別番号」のようなものです。
ところが、この「プロトコルに関係するオブジェクト」というもの自体は、我々が実際にプログラム中に記述して使うものではないのです。
「オブジェクティブCなので、データを取り扱うにはオブジェクト化しなければいけないので、オブジェクトにした」という感じです。
そこで、「データのある場所だけ示しておく」という意味で、「ポインタ」の情報だけ扱うのです。
プログラムの厳密なしくみは気にしないことにして、この「プロトコルに関係する」プロパティの「役目」から考えておきましょう。それはこうです。

「このプロトコルを使って自分(MyModalViewControllerのオブジェクト)が、連携する相手」

Objective-Cでは、メソッドによる処理のことを、「オブジェクトに『このメソッドに書かれた処理をしてください』という『メッセージ』を送る」という考え方をします。ですから、こう考えてもいいと思います。

「このプロトコルに定義したメソッドをやってください、というメッセージの送り先」

ただし、「メッセージ」という言葉は、プログラミング以外にあまりにも汎用的に用いられるので、筆者は文法の説明では多用しないことにしています。今回のアプリケーションでも特に断らない限り、「メッセージ」という言葉は、日常生活における「挨拶文」や「メモ」の意味で使います。
いずれにしろ、そのような相手のことを、「id」という数値情報でもって与えられるのが、「delegate」というプロパティです。


めんどくさいですね。「そういうものだ」となんとなくでも納得が言ったら、あとは「書き方」を覚えてその通りに書いていきましょう。

ヘッダファイル「MyModalViewController.h」 を編集 - 実際

プロパティ delegateの書き表し方

この、ちょっとうさんくさいプロパティ「delegate」をコード上で定義するには、リスト1のようになります。
リスト1 このようなプロパティ「delegate」を定義したい
 id<MyModalViewControllerDelegate> delegate


注意すべきは、プロパティ「delegate」のデータ型は「ポインタに相当するもの」なので、「*」をつけません。通常のオブジェクトだと「myobjectというオブジェクトのポインタです」という意味で「*myobject」とつけるのですが、「id」というデータ型はすでに「なにかのオブジェクトのポインタ」です。それにつけた名前なので、「*」をつけないのです。
たとえば、「清水さんの家」が「茅ヶ崎市」の場合、
shimizu=清水

なので、
*shmizu=茅ヶ崎市
と表すのがポインタです。
しかし、「id」というデータ型は、
shimizuhouse=茅ヶ崎市
のように、いきなりポインタに変数名をつけています。清水さんが住んでいる茅ヶ崎市を指す必要はあるが、清水さんのほうは特に使わない、という感じのときに このように「id」というデータ型に直接変数名をつけて使用します。

「MyModalViewControllerDelegate」というのがあるヨと書いておく

さてここで注意事項です。ファイル「MyModalViewController.h」には、まずクラス「MyModalViewController」の定義、そのあとにプロトコル「MyModalViewControllerDelegate」の定義が書いてあります。
このような場合、先に定義するほうに、あとで定義するほうのことをいきなり書くと、Objective-Cでは、エラーになります。C言語をやっている人は納得が行くでしょう。Javaではその必要はありません。

対処は簡単です。クラスの定義の前(外)に、リスト2のように書いておけばいいのです。コンパイラは、「そういうものがある」ことさえわかれば、細かい定義などはファイルの最後まで読んでから判断してくれます。
リスト2 「こういうものがあるので、あとで読んでください」
 @protocol MyModalViewControllerDelegate;

プロパティの性質は「retain」ではなく「assign」

「deligate」はプロパティですから、プロパティ宣言をします。しかし、その性質は「retain」でなく「assign」にします。
リスト3 「assign」な性質のプロパティ
 @property (nonatomic, assign) id delegate;
「assign」とは、「作ったら、すぐに内容を他のオブジェクトに渡してしまう」場合に使います。まだ実際のコードを書いていないのでピンと来ないかもしれませんが、「デリゲート」とは「他のオブジェクトに使ってもらう」ためのものですから、これもなんとなく合点が行くのではないでしょうか。
以上、プロトコル「MyModalViewController.h」の全文は以下のようになります。
リスト4 プロトコル「MyModalViewControllerDelegate」の定義を付加した「MyModalViewController.h」の内容
#import 

@protocol MyModalViewControllerDelegate;

@interface MyModalViewController : UIViewController {
    
    IBOutlet UITextField *msgText;
    id<MyModalViewControllerDelegate> delegate;
 }

    @property (nonatomic, retain) IBOutlet UITextField *msgText;
    @property (nonatomic, assign) id<MyModalViewControllerDelegate> delegate;

     -(IBAction)showButtonPressed:(id)sender;
@end


@protocol MyModalViewControllerDelegate <NSObject>

    -(void)mymodalviewcontroller:(MyModalViewController *)mymodalviewcontroller didClose:(NSString *)message;

@end

実装ファイル「MyModalViewController.m」を編集 - 方針

プロパティ「delegate」の扱い

「assign」な性質のプロパティではありますが、実装(@synthesize)は必要です。 一方、「retain」な性質と宣言していないので、「release」は不要です。

あとはメソッド「showButtonPressed」の実装のみ

このアプリケーションで、「使ってもらう側」である「MyModalViewController」が「デリゲート」を用いてすることは、ボタンを押したらどうするかを記述する「showButtonPressed」の実装だけです。
「デリゲートを用いる」とは、自身のプロパティとして変数名を設定した「delegate」を用いるということに他なりません。

ボタンを押したら、「誰かに」おまかせ

ボタンを押したらどうするか。現象としては、「このモーダルビューからデータを受け取って、モーダルビューを閉じる」です。
でもそれは、モーダルビューのコントローラがすることではありません。
親である別のビューコントローラがすること...なのですが、モーダルビュー側は、そこまで知る必要がありません。「誰か」がやってくれればいいのです。

「おまかせ」ですが、ただし・・・

さらに、「データを受け取って、閉じる」といった具体的な作業も知る必要がありません。「何を」してくれるかはおまかせです。
でも、「全く関知しない」では、「連携」にはなりません。なにか、共通点は必要です。
その共通点が、「プロトコル」で定義した「メソッド名」ということになるわけです。

そこで、「showButtonPressed」の中身は、以下のような概念になります。

「(ボタンを押したら)誰か、mymodalviewcontroller:didClose: というメソッドをやってください」
「ただし、そのメソッドで何をするかは、その誰かが決めてください」

めんどうなメソッド名の理由

これではまだ連携がハッキリしませんね。「連携」というのは、互いの情報を共通のプログラムに載せることです。
そこでまず、モーダルビューコントローラ側の情報を以下のように載せます。

「なお、このメソッド『mymodalviewcontroller:didClose:』 における『mymodalviewcontroller』とは、私のことです」

これが、複雑なメソッド名の理由なのです。

「delegate」が「プロトコルのメソッド」を呼ぶ

では、「誰かメソッドをやってください」とはどう書くのでしょうか?これは「誰か」を「delegate」としておけばよいので、まさに以下の通りです。

[delegate mymodalviewcontroller:self didClose:相手に渡すデータ]

「delegate」がメソッド「mymodalviewcontroller:didClose」を呼び出します。そのとき、引数「mymodalviewcontrller」に「self(MyModalViewController自身のオブジェクト)」を割当てます。

自分のデータを「誰か」に渡す

メソッド「mymodalviewcontroller:didClose」うち、「didClose:」の引数は上記では「相手に渡すデータ」という表記を仮に置いておきました。このように、モーダルビューのコントローラが、自分が取得した値を引数として親元に渡すことができます。
ここで渡したいデータは、モーダルビュー上の入力欄(プロパティmsgText)に入力した文字列です。そのためにヘッダファイル上で、以下のように定義しています。
 -(void)mymodalviewcontroller:(MyModalViewController *)mymodalviewcontroller didClose:(NSString *)message;
メソッドの2つ目の引数「message」は文字列型、と決めてあります。ですから、ここに「msgText」から取得した文字列を入れてやればいいのです。

これで、「使ってもらいたい側」から言いたいことは、すべて表現することができました。

相手が「あなたと連携するのは私です」と言ってくれる

最後に、気になるのではないでしょうか。「誰か使ってください」というお願いに対して、「私が」と名乗りを上げるにはどうするのか?
これは、「MyModalViewController」クラスの定義に書くことではありませんが、頭に入れておくとコードが書きやすいと思います。
本アプリケーションでは「親」である「ShowMessageVewController」クラスのオブジェクトがそれに当たります。
「MyModalViewController」のプロパティ「delegate」は「連携の相手」と考えればよいことにしました。そこで、「ShowMessageViewController.m」のどこかで、以下のように書いてやればいいのです。

MyModalViewController(のオブジェクト)さん、あなたのプロパティdelegate、それは私です

上記の実際のコードは「ShowMessageViewController.m」の編集時に解説するとして、とにかくこれで2つのビュー・コントローラがつながりました。

実装ファイル「MyModalViewController.m」を編集 - 実際

メソッド「showButtonPressed」の全文

実装ファイルは自動記入の内容が多いので、ファイルの全文を挙げるとかえってわかりにくくなるのでしません。メソッド「showButtonPressed」の全文を挙げるにとどめます。
リスト5 メソッド「showButtonPressed」の実装全文
-(void)showButtonPressed:(id)sender{

    [delegate mymodalviewcontroller:self didClose:msgText.text];

}

プロパティ「delegate」の実装

リスト6のように、MyModalViewControllerの他のプロパティと一緒に、プロパティdelegateも実装しておきます。
リスト5 プロパティ「delegate」の実装
@synthesize delegate;

これで、プロトコルのメソッドを「使ってもらう(MyModalViewController)」側のコードは完成です。
最後に、「使ってあげる側(ShowMessageController)」側で、どのように使ってあげるのかを見て行きましょう。

メソッドを使ってあげる側の設定

「次の一歩」一覧に戻る