2012年4月20日金曜日

Objective-Cにおけるselfとsuperの扱い

iOSの勉強してるのでいくつかメモ。

語りつくされた話なんだろうけど、Objective-Cとは、"C言語上にSmalltalkライクなマクロを実装し、オブジェクト指向を扱えるようにした" とでも表現すべき奇怪な言語である。

ゆえに言語規則として書かれていることでも、実際には言語としてサポートされているわけではなく、構造体やポインタなどC言語の機能で再現されているだけだったりする。ほとんど制約らしい制約がないため、言語規則をぶっちぎった最適化やハックが可能なフリーダムさが魅力。

一見するとオブジェクト指向的なキーワードである、自身を意味するselfや、スーパクラスを意味するsuperの奇妙な振る舞いもそのことが起因している。

self

selfの正体とは、メッセージによってメソッドが呼び出された際に、ランタイムによってレシーバに渡される隠し引数。つまりObjective-Cにおけるselfとは、ただのローカル変数に過ぎないのである。

そして注意すべきこととして、Objective-Cではselfという同じ変数名でありながら、文脈によって異なる中身を返す

インスタンスメソッドではインスタンス自身が渡され、クラスメソッドではクラスオブジェクトが渡される。

クラスオブジェクトとはプログラム実行時にクラスごとに一つだけ生成される、クラスの雛形となるオブジェクトのこと。

javaならばクラスメソッドに相当するstaticメソッドでは、selfに相当するthisキーワードは使えない。しかしOjbective-Cをjavaに例えると、staticメソッドではthisがHogeClass.classのような働きをするのである。

super

ではsuperとは何かといえば、ランタイムがメソッド検索する場所を切り替えるフラグの役割を果たしているのである。

通常のメソッドを呼び出しでは、受け取ったインスタンスのクラスでメッセージを処理する。superはそのメソッドを定義したクラスの親クラスのメソッドを検索してメッセージを処理する。

ゆえに単なるローカル変数のselfでは値を代入したり、selfを返したりできるが、superには代入も返却もできず、できることはメッセージを受け取ることである。「定義されたスーパークラスでメッセージを処理する」という特殊なレシーバとみなすことができる。

selfとsuperのズレ

ゆえにselfとsuperが指すものはクラス階層によってズレが生じる。

SuperHoge、SuperHogeを継承したChildHoge、ChildHogeを継承したGrandchildHogeという3つのクラスを考える。

SuperHogeはhogeメソッドを実装し、これをChildHoge、CrandchildHogeがそれぞれオーバライドして独自の処理を行っているとする。

ここでChildHogeがselfに対してhogeメソッドを呼び出すcallSelfと、superに対してhogeメソッドを呼び出すcallSuperメソッドを実装した時、各々のメソッドが呼び出すhogeメソッドの処理はどのクラスのものになるか?

ChildHogeのときは直感通りになる。すなわち、callSelfではChildHogeがオーバーライドした処理を行い、callSuperではSuperHogeで実装した処理となる。

ではGrandchildHogeが、ChildHogeより継承したcallSelfとcallSuperメソッドを呼び出すとどうなるのか?

callSelfではGrandchildHogeで実装した処理が、callSuperではSuperHogeで実装した処理となるのだ。

重要なのは2点。

  • selfとはメソッドを呼び出したGrandchildHogeインスタンスが代入される変数に過ぎないから、callHogeが[self hoge];を実行したとき指すselfはGrandchildHogeである。
  • superはメソッド定義クラスの親クラスのメソッドを呼び出すスイッチャであるから、ChildHogeインスタンスでもGrandchildHogeインスタンスでも更にその子孫であっても、ChildHogeで定義された[super hoge];が実行するのはSuperHogeクラスの処理である。

言葉で書いていても伝わりにくいので、簡単だし実証コードでも書くべき。この違いはかなり重要だと思った。

0 件のコメント:

コメントを投稿