前回の
実例:
http://swift.sandbox.bluemix.net/#/repl/5955b35d2a36cb5cf81581a4
からの続き。
ラノベ本の原型から抜粋(前回の続き)
「例えば、さっきの勇者の質問を、それぞれの村人に行うならこう書くわけですが、これだと魔法使いには質問できないんですよ」
安堂が指差す画面では、勇者のaskの引数に、ma、ma2を渡したメッセージにはエラーマークがついている。
興味がある人は実際に上の、実例の一番下に追加して試すといいでしょう。
・・・ let m2 = Murabito() let y = Yusha() let ma = Mahotsukai() let ma2 = Mahotsukai() ma2.maho = "爆裂系" // ma2のmahoプロパティを変更 ma.answer() ma2.answer() y.ask(m:ma) 追加 y.ask(m:ma2) 追加
Runするとエラーになります。
で、オブジェクト指向では、これをどう解決するかというと
ラノベ本の原型から抜粋(上の続き)
「askの引数の型が村人クラスなんで、魔法使いクラスのオブジェクトは受け付けてくれないのかな?」
「そうなんです」
「魔法使いクラスにもanswerメソッドがあるけどダメなんだ」
「ダメですね。受け取る引数の型が違うと受け付けてくれません。これは関数でも同じですね。引数の型はそれだけ重要ってことなんですが…」
「魔法使いにも質問できるようにしたければ、勇者側に魔法使い用のaskメソッドを追加する必要がある?」
「そうなんですよ」
「でも、これって、オブジェクトに送るメッセージや、中でやることは全く同じなのに、メソッドは別々って、どうなんだよ?て感じでしょう」
「だね〜」
「こういう時、オブジェクト指向ではクラスの派生という機能が使えるんです」
そう言うと、安堂は魔法使いの定義を書き換えた。
「こう書くことでSwiftは、魔法使いクラスを村人クラスが特化したものと解釈するようになるんです。これをクラスの派生と言います」
魔法使いクラスは、村人クラスから派生した
書き換えと同時に、さっき出ていた場所のエラーマークが消える。
「こうすると魔法使いは村人でもあるので、村人用のaskメソッドに、魔法使いも渡せるようになります」
しかし、新たに魔法使いクラス定義でエラーが表示されていた。その場所を指差しながら安堂が続ける。

「クラスを派生させると、派生元クラスがもつ特徴はそのまま継承されます。派生元が持っているメソッドも、そのまま使えることになるんです」
「ですから魔法使い側のanswerメソッドの記述は不要になるんです。というか、二重定義だと注意されます。これがこのエラーの理由です」
というように、派生という機能を使って「魔法使いも村人の一種ですよ〜」とします。
ただし、これだけだとやっぱりエラーになるんですな。
それが、村人から派生させたことよって、魔法使い側のanswerメソッドが二重定義となる問題。
一番簡単なのは、次のように魔法使い側のanswerメソッドを消すことなんですが
class Mahotsukai { // 魔法使い
var maho = "回復系"
func answer() {
print(maho + "魔法が使えます")
}
}
こうしちゃうと、魔法使いも勇者の問いかけに「村の者です」って答えてしまうんですよ。そのための解決策が〜
ラノベ本の原型から抜粋(上の続き)
「でも、今回は村人のanswerメソッドを、そのまま魔法使い側で使いたくないんですよ。なので、二重定義じゃないよということをXcodeに知らせるために、村人のanswerメソッドにキーワードを追加します」
それがoverrideというキーワードっす。
override func answer() {
これで、勇者のaskメソッド内での「あなたは?」の問いかけに対して、村人は「村の者です」と答えるが、魔法使いは使える魔法を答えるようになる。
ラノベ本の原型から抜粋(上の続き)
「これって、answerメッセージを送った相手が魔法使いだったから、魔法使いクラスでオーバーライドしたanswerメソッドが使われたってこと?」
「そうなんです。askメソッド内ではあくまで村人として扱われるんですが、実際は村人でもあるが魔法使いでもあるので、魔法使いの振る舞いをしたんですね」
メッセージを送る相手によって振る舞いが異なる
「これがメソッドと関数の大きな違いであり、オブジェクト指向プログラミングの最大の特徴でもあるんです。iPhoneアプリのプログラムは、このオブジェクト指向プログラミングの特徴を使ってるんですよ。例えば、このtestプログラムなんですが…」
そう言うと、今度は一昨日のSingle View Applicationテンプレートを選んで用意したtestプロジェクトを開く安堂。そして左のリストからViewController.swiftを選ぶ。
「で、ここでさっきの継承の話です。ViewControllerのクラス定義を見てください」
「ViewControllerは、UIViewControllerを派生させたオブジェクトなのか。てことは、何もオーバーライドしなければUIViewControllerとして振る舞うことになる?」
「そうなんです。じゃあ、その派生元のUIViewControllerは、一体何をするオブジェクトかというと、iPhoneの画面を表示してユーザーと対話するオブジェクトなんですよ」
「さっき説明したイベント駆動ループでのイベントが、このUIView、その持ち主のUIViewControllerによって処理されるんです」
ここで出てくる、イベント駆動ループの話は別のところでやってるんですが、ゲームでもなんでも、ユーザーと対話するアプリは大概、このイベント駆動ループという形式で作られてます。
簡単にいうとユーザーからのキー入力といったイベントを受けて、それに対応するという処理を永遠に繰り返す形式です。でもってイベントの一つに、アプリ終了というものがある。
iPhoneのアプリもこのイベント駆動ループの形式で作られてます。
で、XcodeでSingle View Applicationテンプレートを選んで用意したtestプロジェクトでは、アプリが起動されるとイベント駆動ループに入って、その中でViewControllerオブジェクトを用意するまでを自動でやってくます。
で、ViewControllerは前回見たように、testプロジェクト内に存在する、自分たちが定義するオブジェクトってことになっているんですが。Appleが用意してるUIViewControllerの派生としてるんで、何も書かなければUIViewControllerとして振舞うことになるわけです。
ラノベ本の原型から抜粋(上の続き)
ただし、と言って安堂は続ける。
「ここで実際にメッセージを受け取っているのは、UIViewControllerから派生したViewControllerなので、僕らの方で動作を書き換えることができるんですよ。例えば、もし表示する画面をカスタマイズしたければ、ViewController側で、UIViewController側が持っている、画面を準備するメソッドを…」
「オーバーライドすればいい?」
「そういうことです。これがSingle View Applicationテンプレートで用意されるアプリの構造てことになります」
「じゃあiPhoneアプリで、このViewControllerクラスの定義で、派生元のメソッドをオーバーライドすれば、独自のアプリを作れるってことか?」
「そうなります」
ボタンやウィンドウ、メニューといったグラフィカルなユーザーインターフェースをもつアプリ(GUIアプリ)は、メニューバーをクリックすればメニューが現れ、ウィンドウのタイトルバーを持てば、自由にウィンドウの位置を移動させたりできるが、そういった決まりきった動作を毎回プログラムするのは効率が悪い。
1からアプリを組み立てるのではなく、最初にベースとなるキットを用意しておき、プログラマがこれを部分部分でカスタマイズできる仕組みがあれば…
その仕組みをオブジェクト指向の派生とオーバーライドで実現するのが、現在のGUIアプリの作り方の主流だった。
iPhoneアプリの開発でもそれは変わらない。
「そういった派生やオーバーライドが主体になってるから、今朝俺が書いたプログラムとまるっきり違うことになるのか」
「そうなんです」
例えば、ViewController.swiftでオーバーライドしてるviewDidLoadメソッドの記述に次にように数行の変更を加えると〜

真っ白い画面に文字列が表示されるようになります。
↓Single View Applicationテンプレートを選んでプロジェクトを用意する手順はこれね
↓上の手順で作られるプロジェクトがこれ(一応置いておく)
http://tetera.jp/xcc/book-sample/test.zip
↓viewDidLoadメソッドに追加記述したプロジェクトがこれ











