Swiftで遊ぼう! - 260 - プロトコールとデリゲーション ProtocolsとDelegation
- Swiftで遊ぼう!の前書き-> Life-LOG OtherSide
- 初心者はここから!-> 50オヤジでもできるiOS開発
- 私の本業、オフィシャルなブログ-> Life-LOG
2019年5月30日;Swift5を利用して、Appleの推奨するプロトコール指向プログラミング(POP)の解説に変更
2016年10月17日:Swift3向けにイメージを変更*1
この記事を書いていた頃は、オブジェクト指向プログラミング(OOP)の理解に苦しんでいました。WWDC2015で、protocolとextensionを使った、protocol-oriented programming(POP)、プロトコール指向プログラミングが紹介され、OOPの理解をしたところだったので、POPの考え方も理解できました。OOPはクラスの拡張にしか使えませんが、POPで実装していけば、クラスだけに囚われず、あらゆる型の拡張に使えるから便利です。そして、コードの可読性もあがります。今日からPOPにプログラミングスタイルを変えていきましょう。以下にOOPの実装とPOPの実装法を並べて説明します。
FaceView(V)とHappinessViewController(C)を繋げていくためにプロトコールとデリゲーションの復習をします。
プロトコールの復習。これで何度目になるだろう?
復習をすればするほど理解は深まっていく実感はありますが、ペースが非常に遅いんで情けないです(T_T) このブログに読者が5人*2いるけど、「何やってんだ。おせーな。つきあってらんねーよ」という言葉が聞こえてきそうです(^^;)
今までのプロトコールとデリゲーションに関する記事を並べてみると...
- Swiftで遊ぼう! - 27 プロトコール、イニシャライズで疲れました:Life-LOG OtherSide
- Swiftで遊ぼう! - 28 プロトコールの理解も時間がかかりそう。:Life-LOG OtherSide
- Swiftで遊ぼう! - 31 プロトコールをなんとかして理解したい...:Life-LOG OtherSide
- Swiftで遊ぼう! - 74 オブジェクト指向1 ポリモーフィズム:Life-LOG OtherSide
- Swiftで遊ぼう! - 111 - Swift Fundamentals本 プロトコール:Life-LOG OtherSide
- Swiftで遊ぼう! - 134 - プロトコールとデリゲーション 2:Life-LOG OtherSide
- Swiftで遊ぼう! - 135 - プロトコールとデリゲーション 3:Life-LOG OtherSide
- Swiftで遊ぼう! - 136 - プロトコールとデリゲーション 4:Life-LOG OtherSide
まだまだデリゲーションを完全に自分のモノにしたとは言いがたいのですが、今回ポール先生の講義を聴いて、デリゲーションの実装の仕方は理解できたと思います。
Viewクラス(UIViewクラス)とControllerクラス(UIViewController)の関係性を使ってプログラムする例を考えてみたら理解しやすかったです。
OOPによる プロトコール・デリゲーション実装法
UIViewクラスのオブジェクト(インスタンス)はジェネリック(一般的)な存在なので表示機能は備わっていても、何を表示するか具体的なコンテンツを知る必要はありません。そのため、コンテンツの内容に関して他のオブジェクトに任せる必要があります。この任せることをデリゲーション(委譲)と呼びます。ちょっと分かりにくいのですが、処理を任されたオブジェクトのことをデリゲートと呼びます。すなわちUIViewControllerクラスのオブジェクトがデリゲートということです。日本語が妙なんですが気にしないで、デリゲーションの説明をします。
2つのクラスをカスタムに
- AViewクラスがAControllerクラスに任せたい(デリゲーション)処理をデリゲート・プロトコールとして用意します。ViewクラスにあるcontrolValueを外部のオブジェクトで操作してもらいたい(委任)のですが、この値を操作する関数「getControlValue(sender:)」をプロトコールとしてパブリックにオープンにします。
- デリゲート・プロトコールを型として、デリゲート・プロパティをAViewクラス内で保持します。初期化ステップが発生するまで変更が加わらないようにするためにオプショナル設定にします。また循環参照を避けるためにweakにしてメモリーリークを回避します。
- デリゲート・プロパティにAViewである自分自身(self)を引数として与えることで、controlValueの具体的な値を取り出してきます。ここを理解できるとプロトコールとデリゲーションの関係が理解できたと言えるでしょう。基本的にdelegateというプロパティは、AControllerクラス内で自分自身である(AController)を指定しているため、このdelegateはAControllerクラスで記述している「getControlValue(sender:)」メソッドのみを持つ「AViewDelegateData型」になっているんです。ということで「delegate?.getControlValue(sender: self)」 でAControllerで記述されているメソッドを利用して具体的なInt型の値を取り出すことができます。
- 処理を委任(デリーゲート)されたAControllerクラスに先ほどのプロトコールを準拠させます。
- 先ほど説明したAController自身をAViewクラスのプロパティ(delegate)に指定することで、delegateは「AViewDelegateData型」になります。このステップは、コードを書いて実装する場合に必ず必要ですが、Xcode8を使って設定する方法もいくつかあります。次のリンクに説明があります。Swiftで遊ぼう! - 318 - My Picker Project : SingleComponentPicker Xcodeでデリゲーション - Swiftで遊ぼう! on Hatena
- プロトコールはブループリントなので、ここで具体的な処理をコードします。
以上! 異なるクラスをプロトコールを使って繋げることができます。
重要:iOS API利用時は、上記のステップ(1-3)を必要としません。例えば、UIViewクラスやサブクラスなど、ジェネリックなクラスは、表示機能のみ受け持ち、コンテンツの内容と表示に関わる具体的な制御をデリゲーション(委譲)させるため必要なプロトコールを既に用意しています。「DataSource」と「Delegate」という2つのプロトコールがそれです。それぞれ同じデリゲートオブジェクトを指していますが、メソッドが異なります。「DataSource」はコンテンツに関わるもの(表示内容や項目数など)で、「Delegate」は表示に関わるもの(表示位置や配置)です。必ず2つのプロトコールを準拠させて、オプショナル以外の必須メソッドを記述する必要があります。
POPによる プロトコール・エクステンション実装法
UIViewクラスはジェネリックな存在ということに変わりはありません。表示機能しか備わっていないので、具体的なコンテンツは、UIViewConrollerにデリゲーションする必要があるので、そのための機能をプロトコールとして用意します。このステップは、OOPと同じです。
- AViewクラスがAControllerクラスに任せたい(デリゲーション)処理をデリゲート・プロトコールとして用意するところは、OOPと全く同じです。
- プロトコール型の変数プロパティをUIViewに持たせるところもOOPと同じです。そして、このプロパティ型の変数は内部にAViewを保持するので循環参照が生じます。メモリーリックを回避するためにweakにします。
- AViewである自分自身(self)を引数として与えることで、controlValueの具体的な値を取り出す循環参照になっています。
- POPでここが大きくことなります。AControllerクラスはプロトコールに準拠する必要が無いので、クラス内部で具体的な実装コードを書く必要がありません。コードをシンプルにできて間違えにくくなります。
- このステップはOOPと同様です。プロトコール型のAVIewクラスをコントローラが 持って、プロトコール型のdelegateプロパティに自分自身を指定するため、当然、循環参照なのでweak指定です。
- AControlerにプロトコール型を準拠させるためにエクステンションを使って拡張させる訳です。
さあ、OOP、POP、どちらでも問題無く理解できましたよね。