Objc01

1,537 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,537
On SlideShare
0
From Embeds
0
Number of Embeds
861
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Objc01

  1. 1. 第10回iPhone輪講 CHAPTER02
  2. 2. CHAPTER02 Objective-Cのプログラム
  3. 3. オブジェクトとメッセージ メッセージ式 オブジェクトはどのクラスに属するものでもidという特別な型で表現される オブジェクトを格納するための変数objの宣言は以下のようになる あるオブジェクトにobjにメッセージmsgを送る事を のように記述しメッセージ式(message expression)と呼ぶ id obj; [ obj msg ]
  4. 4. オブジェクトとメッセージ メッセージ式は,レシーバであるオブジェクトがそのメッセージを処理した結果の値を返す メッセージ式の扱いはCの関数呼び出しと同じ メッセージ式は別の式の構造要素としても使う事ができる void型を返すメッセージ式もある
  5. 5. オブジェクトとメッセージ メッセージの送り先(objと書いた部分)には,オブジェクトを表す式も記述できる 従って,あるメッセージ式の結果がオブジェクトなら,以下のように さらに続けてメッセージを送る事が出来る Cでは[ ]は配列の要素を参照する演算子だが,Objective-Cではメッセージ式を 表すために使っている 配列の演算子かどうかは,左側に配列名やポインタなど別の式の有無で 区別できる [[ obj msg1 ] msg2]; [[ obj msg1 ] msg2 ] msg3 ];
  6. 6. オブジェクトとメッセージ メッセージ式の値をそのまま配列の添字として使う場合は以下のようになる メッセージは変数名のような識別子と同様なルールで作られたメッセージキーワード から構成されている 関数呼び出しと同様にメッセージには引数を付ける事もできる 引数の無いメッセージは,1つのメッセージキーワードだけからなる element = table[[ obj count ]];
  7. 7. オブジェクトとメッセージ 以下に例を挙げる 2つ目の例にあるように,変数名とメッセージキーワードは名前が重複していても構わない メッセージ式の中ではメッセージキーワードかどうか区別できる [aString copy]; width = [node width]; [[doc filename] retain;
  8. 8. オブジェクトとメッセージ メッセージに引数がある場合はメッセージキーワードの末尾に「:」を付けて引数が ある事を示す 「:」の次には実引数がくる.実引数は関数呼び出しと同様に一般の式が記述できる 別のメッセージ式を実引数とする事も出来る [printInfo setLeftMargin: 60.0]; [[[cw window] firstResponder] copy: sender; [doc isSameDirectory:[info objectAtIndex: ++num]];
  9. 9. オブジェクトとメッセージ 2つ以上の引数がある場合は「:」を付けた別のキーワードを追加する あるいはキーワードなしで「:」だけを追加することも文法上は許されている 逆に「:」が付かないキーワードを追加する事は無い [cell = [albumview cellAtRow:i column: j]; [manager fileExistsAtPath:dirname isDirectory:&isdir]; [view lineTo: 1.4142 : (y + 1.0)];
  10. 10. オブジェクトとメッセージ メッセージセレクタ 関数を関数名で呼ぶように,個々のメッセージはキーワードを書き並べたものを名前として 表し,他のメッセージと区別する これをメッセージセレクタ(message slector),あるいはセレクタ(selector)と呼ぶ メソッド名と呼ぶ事もある
  11. 11. メッセージセレクタ メッセージ式の例にあったいくつかのメッセージは以下のようなセレクタを持つ 引数を持つキーワードは「:」もセレクタに含める事に注意する 以下の例ではcopyとcopy:は異なるセレクタになる copy retain firstResponder copy: objectAtIndex: cellAtRow:column: fileExistsAtPath:isDirectory: lineTo:
  12. 12. Objective-Cのプログラム インスタンスの生成と初期化 id型の変数を宣言しただけでは,その変数には何のオブジェクトも格納されていない オブジェクトに仕事をしてもらうには,まずクラスからインスタンスを生成する事が 必要となる Objective-Cではクラスにメッセージを送る事でインスタンスの生成を行う
  13. 13. インスタンスの生成と初期化 クラス名に対してメッセージallocを送る事により,新しいインスタンスが1つ作成される このように生成されたインスタンスは,メモリ上に必要な領域が確保されただけ 通常はこの直後に初期化を行う必要がある 初期化のためのメソッドはイニシャライザ(initializer)と呼ぶ イニシャライザとしてどのようなメソッド用いるかは各クラスによって異なる [クラス名 alloc]
  14. 14. インスタンスの生成と初期化 Cocoaでは通常,イニシャライザはinitか,initから始まるメソッド名を持つものである という約束がある あるクラスからインスタンスを作成するメッセージ式の典型例 インスタンスの生成は常にallocのメッセージ式に直接イニシャライザを 適用する形式で書く [[ クラス名 alloc ] init ]
  15. 15. インスタンスの生成と初期化 初期化はインスタンスの情報を「リセット」するものではない イニシャライザによる初期化はそのインスタンスが生成された直後に一度だけ行う リセットに相当する処理(インスタンス変数の値を初期値に戻す)が必要な場合は イニシャライザとは別なメソッドとして実現する
  16. 16. クラス定義 クラスのインタフェース部 インタフェース部にはそのクラスのインスタンンス変数 とメソッドを宣言する 通常はヘッダファイルとして作成し,そのクラスを使 うモジュールから参照できるようにする @interface クラス名 : スーパークラス名; { インスタンス変数の宣言; ... } メソッドの宣言; ... @end
  17. 17. クラスのインタフェース部 @interfaceと@endはObjective-Cで導入された コンパイラ指示子と呼ばれる識別子 インタフェースの始まりと終わりを表している 他にもコンパイラ指示子はいくつかある すべて「@」で始まり,従来のC言語の部分と 区別できるようになっている @interface クラス名 : スーパークラス名; { インスタンス変数の宣言; ... } メソッドの宣言; ... @end
  18. 18. クラスのインタフェース部 クラス名にはC言語などと同様のルールに従う識別子を 用いる 先頭を大文字にした単語を並べたものをクラス名と する習慣がある クラス名は変数名や関数名と重なってはいけない @interface クラス名 : スーパークラス名; { インスタンス変数の宣言; ... } メソッドの宣言; ... @end
  19. 19. スーパークラスについては「継承の概念」で説明する 今の段階では「NSObject」と書く事にしておく @interface クラス名 : スーパークラス名; { インスタンス変数の宣言; ... } メソッドの宣言; ... @end
  20. 20. クラスのインタフェース部 インスタンス変数の宣言はC言語の変数定義と同様に 「型�変数名」と記述しておく id型のインスタンスも使える この変数名はクラス内のあらゆるメソッドから 参照されるのでaaaのような単純なもの,意味不 明な名前は避けるようにする @interface クラス名 : スーパークラス名; { インスタンス変数の宣言; ... } メソッドの宣言; ... @end
  21. 21. クラスのインタフェース部 メソッドの宣言は,C言語の関数のプロトタイプ宣言に 相当するもの まず引数が無く,返り値にオブジェクトを返すメソッド の宣言は以下のように書く 返り値の型は()で囲んだ型名をメソッドの前に置く ここではメソッド名をdelegateだとする @interface クラス名 : スーパークラス名; { インスタンス変数の宣言; ... } メソッドの宣言; ... @end - (id)delegate;
  22. 22. クラスのインタフェース部 仮引数の型の指定も同様に()で囲んだ型名を仮引数 の前に置く 2つの整数を引数とし,返り値としてオブジェクトを返す メソッドcellAtRow:column:の宣言は以下のようになる rowとcolは仮引数 値を返さないメソッドにはCと同様にvoid型を指定する @interface クラス名 : スーパークラス名; { インスタンス変数の宣言; ... } メソッドの宣言; ... @end - (id)cellAtRow:(int)row column:(int)col; - (void)setAutodisplay:(BOOL)flag;
  23. 23. クラスのインタフェース部 メソッドの宣言は,C言語の関数のプロトタイプ宣言に 相当するもの まず引数が無く,返り値にオブジェクトを返すメソッド の宣言は以下のように書く 返り値の型は()で囲んだ型名をメソッドの前に置く ここではメソッド名をdelegateだとする @interface クラス名 : スーパークラス名; { インスタンス変数の宣言; ... } メソッドの宣言; ... @end - (id)delegate;
  24. 24. クラス定義 クラスの実装部 クラスの実現部分は実装部(インプリメント部)と呼び, 右のように記述する @implementationは実装部の始まりを示すコンパイラ 指示子です 実装部にはスーパークラス名,インスタンス変数は 記述しない @implementation クラス名 メソッドの定義 ... @end
  25. 25. クラスの実装部 インタフェース部で宣言したメソッドは実装部で必ず定 義しなければならない インタフェース部でメソッドを宣言しなかった場合 実装部にメソッドを全く定義しない事も出来る @implementation クラス名 メソッドの定義 ... @end
  26. 26. クラスの実装部 メソッドの定義は,メソッドの宣言と同じ記述の後に Cの関数本体の定義と同じ形式のブロックを置いたもの 簡単なメソッドの定義の例は以下 @implementation クラス名 メソッドの定義 ... @end- (double)evalution:(int)val { double tmp = [order proposedBalance: val]; if (currentValue > (int)tmp) tmp = [order proposedBalance: val * 1.25]; return tmp; }
  27. 27. クラスの実装部 メソッド内からはそのクラスのインスタンス変数に自由にアクセス可能 例ではorderとcurrentValeはインスタンス変数 メソッド内局所変数の定義方法はこの関数と同様 ただしインスタンス変数と同じ名前で定義すると,インスタンス変数が隠されて アクセスできなくなる メソドの仮引数も同様でインスタンス変数と同じ名前にするのを避ける
  28. 28. クラスの実装部 メソッド定義の中で,そのメソッドを使っているインスタンスオブジェクト自身を表している場 合はselfという名前を使う selfは変数に代入したり,メソッドの返り値として返す事も出来る メソッドの再起呼び出しも可能
  29. 29. クラス定義 クラス定義の例 リモコンでコントロールできるテレビなどのボリュームをシミュレートする クラスVolumeを考える このクラスには最小値,最大値,および変化の刻み幅を初期設定するメソッド, 値を1レベル上げるメソッド,下げるメソッドがある 各インスタンスは音量として,最小値から最大値までの間どれかの値を持ち, volumeというメソッドでその値を調べる事が出来る
  30. 30. クラス定義の例 List2-1 Volumeクラスのインタフェース部
  31. 31. List2-2 Volumeクラスの実装部
  32. 32. クラス定義の例 [super init]はスーパークラスであるNSObjctの初期メッセージの呼び出しを 表している nilはオブジェクトとして無効な値であることを示す定数として定められている スーパークラスからの返り値が無効なら初期化は行わない
  33. 33. クラス定義の例 initWithMin:max:step,up,downの3つのメソッドが返り値としてselfを返している事に 注意する selfはメッセージを受け取って,そのメソッドの処理をしているインスタンスオブジェクト そのもの 返り値に対して続けてメッセージを送る事が出来る 変数objにVolumeクラスのインスタンスが格納されているとすると, 以下のような式が実行できる [[[obj up] up] up]
  34. 34. クラス定義の例 クラスの定義だけしても,プログラムとしては動作しない Objective-CもC言語と同様に関数main()が必要 以下List2-3 Volumeクラスをテストするmain関数
  35. 35. コンパイル 簡単なコンパイル方法 List2-1~2-3のプログラムを一つに まとめる ただしインタフェースが他の2つより 先になければならない Objective-Cのソースプログラムを 格納しているファイルは拡張子を「.m」 とする @interface Volume : NSObject   /* List2-1 */ @end @implementation Volume            /* List2-2 */ @end int main(void) {    /* List2-3 */ } #import <Foundation/NSObject.h> #import <stdio.h> 図2-1:簡単なファイルの構成
  36. 36. 簡単なコンパイル方法 Cocoaではシステムが提供するクラスや関数はフレームワーク(framework)という 構造を持った動的なライブラリとして提供される ここではFoundationというフレームワークを使うので,コンパイルオプションとして このフレームワークを使うということを指示しなくてはならない コンパイルは次のように行う -frameworkというオプションがフレームワークの指定 % cc voltest.m -framework Foundation
  37. 37. 簡単なコンパイル方法 これで実行ファイルとしてa.outが作成される ソースファイルがCかC++かObjective-Cなのかは拡張子で区別できる なので特に細かい指定をする必要が無い 実行ファイル名を指定する場合は「-o」オプションを使う コンパイル時警告を表示させたいときは「-Wmost」オプション または「-Wall」オプションを使う 実行結果は以下のようになる
  38. 38. コンパイル 分割コンパイル Objective-Cでは各クラスごとに別々のファイルとして作成するのが普通 クラスを1つ作成するごとに,以下の2つのファイルを作成する 1.インタフェースファイル インタフェース部を記述 ファイル名は「クラス名.h」 2.実装ファイル 実装部を記述 ファイル名は「クラス名.m」
  39. 39. 分割コンパイル インタフェース部はヘッダファイルになる そのクラスを使う別のソースファイルで,そのヘッダファルをインポートする 実装ファイルでもインタフェースの情報を知らなければならないので, インタフェースファイルは必ずインポートする インタフェースを定義するにはスーパークラスで指定したクラスについての情報が必要 例題のプログラムの場合「Foundation/NSObject」というファイルを インタフェースファイル内でインポートしなければならない
  40. 40. 分割コンパイル 先ほどのmain関数にはクラス定義は含まれていないが,関数内でメッセージ式を 使っている メッセージ式はObjective-C特有のもの main関数を含むファイルをC言語のプログラムとしてコンパイルすることは 出来ない クラス定義が含まれていなくても,クラスを参照したり,メッセージ式を使っている場合 そのソースファイルの拡張子は「.m」にしておく必要がある ここではmain関数を含むファイルの名前をmain.mとする
  41. 41. 分割コンパイル これらのコンパイルはCプログラム における分割コンパイルと同様 コンパイルからリンクまで一度に行い 実行ファイル名をvolとするには以下 のようになる オブジェクトファイル(拡張子.o)を 作成してから,リンクして実行ファイル を作成する場合 #import <Foundation/NSObject.h> @interface Volume : NSObject /* List2-1 */ @end #import Volume.h @implementation Volume /* List2-2 */ @end #import Volume.h #import <stdio.h> int main(void) { /* List2-3 */ } Volume.h Volume.m main.m 図2-2:クラス定義を別ファイルに格納した構成 % cc -o vol main.m -framework Foundation % cc -c main.m % cc -c volume.m % cc -o vol main.o volume.o -framework Foundation
  42. 42. プログラムの書き方について ハイブリッド言語 Objcetive-CはC言語にオブジェクト指向を取り入れた言語 完全にオブジェクト指向風に記述する事が出来る 従来通りCの記述とオブジェクトを混在させる事も出来る このような性質からObjective-Cはハイブリッド言語と呼ばれることもある
  43. 43. ハイブリッド言語 Objective-CのプログラムにC言語の関数を組み合わせるのは以下のような 場合が考えられる すでに安定して動作している関数モジュールをそのまま使いたい場合 UnixのシステムコールなどC言語で記述されたインタフェースを使う場合 特定のオブジェクトに関係しない数学用,計算用ルーチンである場合 クラス定義で他のメソッドの下請けとして使う場合 実行速度が特に問題となる場合
  44. 44. ハイブリッド言語 Objective-Cではソフトウェア全体の枠組みはオブジェクト指向で記述し, 必要とされる部分をCの関数で記述するという自由度の高いプログラミングを 行う事が出来る
  45. 45. ハイブリッド言語 Objective-Cでは関数の中から,メソッド定義の中からも自由にCの関数を呼び出す事が 出来る 関数の中からメソッドを呼びだす事が出来る 引数や返り値がid型であるような関数も作成できる
  46. 46. Cの関数定義のみを含むソースファイルは通常のCのプログラム同様,Cを拡張子とする ファイルとして作成する ただし関数の中でメソッドを呼びだしたり,クラス定義に関する情報を 使用したりする場合はCのソースファイルとしてコンパイルできないので 拡張子を「.m」としておく必要がある 同時に対応するクラスのインタフェースを含むヘッダファイルをインポートしなければ ならない
  47. 47. ハイブリッド言語 メソッド定義内から関数を呼び出す場合には,その関数に関する情報 つまりプロトタイプ宣言が参照できていれば十分 拡張子が「.c」であるファイルと「.m」であるファイルは問題なく一緒に コンパイル,リンクできる 例えば以下のようにして実行ファイルを作成できる % cc -o sample main2.m util.c Volume.m -framework Foundation
  48. 48. ハイブリッド言語 クラス実装ファイル内に関数定義を記述する 事も出来る ただしメソッドの定義の下請け関数など そのクラスの実装に直接関係のあるもの に限定すべき 関数の定義は@implementationと@endの 間でも,その前後でも,どこにでも書く事が 出来る #import MyExample.h static void funcA(int a, char *p) { } @implementation MyExample static double funcB(int n) { } - (id)myMethod { } ... @end static id funcC(id obj) { } /*関数定義*/ /*関数定義*/ /*関数定義*/ /*メソッド定義*/ 図2-3:実行ファイル内の関数定義
  49. 49. ハイブリッド言語 実装ファイル内で局所的な関数なので static指示子を用いた方がよい 関数funcCには先行するプロトタイプ宣言が 必要 #import MyExample.h static void funcA(int a, char *p) { } @implementation MyExample static double funcB(int n) { } - (id)myMethod { } ... @end static id funcC(id obj) { } /*関数定義*/ /*関数定義*/ /*関数定義*/ /*メソッド定義*/ 図2-3:実行ファイル内の関数定義
  50. 50. ハイブリッド言語 実装ファイル内で定義した関数であっても その関数の内部からインスタンス変数や selfを参照することはできない 関数への実引数として渡す事は出来る インスタンス変数やselfにアクセスするような 手続きはメソッドとして定義すべき #import MyExample.h static void funcA(int a, char *p) { } @implementation MyExample static double funcB(int n) { } - (id)myMethod { } ... @end static id funcC(id obj) { } /*関数定義*/ /*関数定義*/ /*関数定義*/ /*メソッド定義*/ 図2-3:実行ファイル内の関数定義
  51. 51. ハイブリッド言語 そのクラス全体で共通する機能や手続きは 関数として定義するよりも, クラスメソッドとして定義する方がふさわしい 事がある #import MyExample.h static void funcA(int a, char *p) { } @implementation MyExample static double funcB(int n) { } - (id)myMethod { } ... @end static id funcC(id obj) { } /*関数定義*/ /*関数定義*/ /*関数定義*/ /*メソッド定義*/ 図2-3:実行ファイル内の関数定義
  52. 52. プログラムの書き方について 静的な変数定義 関数やメソッドの定義外で定義された変数および,static指示子を付けて定義した変数は 静的な変数(プログラム実行開始から終了まで存在し続ける変数)になる メソッドの定義内でもstatic付きの静的な変数を利用することはできるが, 通常のCプログラミングとは違うので注意
  53. 53. 静的な変数定数 インスタンスオブジェクトは複数個作られる可能性があるが,静的変数の宣言は 1ヶ所にしかない 従って静的な変数は複数個のインスタンスオブジェクトで共有されることになる あるインスタンスが値を格納して,次にそれを参照するまでの間に別のインスタンスが 値を変更してしまうという事も起こる
  54. 54. プログラムの書き方について ヘッダファイルのインポート Objective-Cでは#importというプロセッサ制御行を使う これは#includeと同じだが,#importは一度読み込んだヘッダファイルを 複数回インクルードしないという特徴がある
  55. 55. 継承の概念 スーパークラスとサブクラス あるクラスを定義するとき,そのクラスが既に定義されている別のクラスの機能を 拡張したり,一部を変更したりしたものである場合もよくある 既に存在する別のクラスの定義を引き継いで新しいクラスを定義できれば 必要な部分だけ書き足せばよいので簡単に定義が行える このように別のクラスの定義の一部を拡張あるいは変更して新しいクラスを 定義する機能を継承(inheritance)という
  56. 56. CHAPTER03 継承とクラス
  57. 57. スーパークラスとサブクラス 新しく作るクラスから見て継承の元となるクラスをスーパークラス(superclass) 逆に継承の元となるクラスから見て新しく作るクラスをサブクラス(subclass)と呼ぶ サブクラスは スーパークラスが持っていた定義をすべて引き継ぐ スーパークラスが持っていたインスタンス変数を持つ スーパークラスと同じメソッドが実行できる
  58. 58. スーパークラスとサブクラス サブクラスはさらに 新しいメソッドを追加できる 新しいインスタンス変数を追加できる スーパークラスのメソッドを別の定義で置き換えられる という変更が加えることが出来る ただし,メソッドの変更なしに新しいインスタンス変数の追加だけ行っても 意味が無い スーパークラスのメソッドを新しい定義で置き換える事を上書き(override)と呼ぶ
  59. 59. スーパークラスとサブクラス クラスBはクラスAを継承し, 同じインスタンス変数とメソッドを 備えているがmethod2は別の定義で 置き換えられている 変数x method1 method2 変数x method1 method2 変数x method3 method2 method1 クラスA クラスC クラスB 変数z 継承 継承 図3-1 継承の概念
  60. 60. スーパークラスとサブクラス クラスCもクラスAを継承しているが 新たに変数zとmethod3が付け加え られている この3つのクラスのインスタンスはどれも クラスAのインタフェースに記述された method1,method2に対応できる事に 注意 変数x method1 method2 変数x method1 method2 変数x method3 method2 method1 クラスA クラスC クラスB 変数z 継承 継承 図3-1 継承の概念
  61. 61. 継承の概念 クラス階層 ある有用なクラスがあると,それを継承して新しいクラスを作ったり, 継承を使って定義したクラスをさらに継承して別のクラスを作ったりと言う事を 繰り返す事がある 結果スーパークラス,サブクラスの関係が木のような階層関係に成長する事がある これをクラス階層(class hierarchy)と呼ぶ
  62. 62. クラス階層 クラス階層をさかのぼると,それ以上スーパークラスを持たないクラスに行き着く これをルートクラス(root class)と呼ぶ CocoaにはルートクラスとしてNSObjectが用意されている 他のクラスは直接,あるいは間接にNSObjectを継承する 自分でクラスを作る場合もNSObjectあるいは既存のクラスをサブクラスとして 定義しなければならない
  63. 63. クラス階層 Root Class Class A Class D Class E Class B Class C Class M Class F Class L Class KClass G Class H Class N Class O Class J 図3-2 クラス階層の概念
  64. 64. 継承を用いたクラス定義 継承関係の宣言 あるクラスを継承して,サブクラスを新たに 定義したい場合 Objective-Cではサブクラスの インタフェース部で継承関係を 宣言する @interface クラス名 : スーパークラス名 { インスタンス変数の宣言; … }    メソッドの宣言; … @end
  65. 65. 継承関係の宣言 あるクラスAのサブクラスBを新たに定義 する場合 クラス名に新しいクラスの名前としてB スーパークラス名に継承したい スーパークラスの名前であるAを書く B A @interface クラス名 : スーパークラス名 { インスタンス変数の宣言; … }    メソッドの宣言; … @end
  66. 66. 継承関係の宣言 Objective-Cではすべてのクラスはルートクラスを継承しなければ オブジェクトとして振る舞う事が出来ない 継承しているクラスがはっきりしている場合には,そのクラス名をスーパークラス名として 記述すればよい 特に継承すべきクラスが無い場合,NSObjectクラスをスーパークラスとして 指定しなければならない
  67. 67. 継承関係の宣言 「インスタンス変数の宣言」にはサブクラスで 新たに付け加えるインスタンス変数のみを 記述する 追加するインスタンス変数がない場合 {}の対だけを書くか,あるいはそれも 省略する @interface クラス名 : スーパークラス名 { インスタンス変数の宣言; … }    メソッドの宣言; … @end
  68. 68. 継承関係の宣言 「メソッドの宣言」もサブクラスで追加するも のだけ記述すればよい スーパークラスで定義されているメソッド をサブクラスで変更(上書き)したい場合 インタフェース部にも改めて記述し, 変更されている事を表すコメントを付けて おくとわかりやすい @interface クラス名 : スーパークラス名 { インスタンス変数の宣言; … }    メソッドの宣言; … @end
  69. 69. 継承関係の宣言 図3-1のクラスAを継承したクラスBを定義する場合のインタフェース部は 変数x,メソッドmthod1はクラスAの宣言を継承するので記述しない メソッドmethod2も記述しなくてよい @interface B : A - (void)method2; //このメソッドは上書き @end
  70. 70. 継承関係の宣言 クラスCを定義する場合のインタフェース部は 変数z,method3の宣言を記述しなければならない @interface C : A { id z; } - (void)method3; @end
  71. 71. 継承を用いたクラス定義 クラス定義とヘッダファイル 分割コンパイラの説明で 「インタフェース部はひとつのヘッダファイルとして作成するのが普通」とあった 継承にとってもこの事は重要
  72. 72. クラス定義とヘッダファイル あるクラスAlphaが既に定義されていたとする Alpha.hというヘッダファイルが存在する このクラスAlphaを継承してBetaを定義する場合 Beta.hでAlpha.hを継承しなければならない
  73. 73. クラス定義とヘッダファイル スーパークラス名で指定されたクラスがどんなものかという情報が無くては サブクラスの定義が出来ない このためにスーパークラスのインタフェース部を含むヘッダファイルが必要になる
  74. 74. クラス定義とヘッダファイル #import <Foundation/NSobject.h> @interface Alpha : NSObject { … } - (void)doSomething; @end #import Alpha.h @interface Beta : Alpha { … } … @end #import Beta.h @interface Gammma : Beta { … } … @end #import Alpha.h … @implementation Alpha … - (void)doSomething { … } … @end #import Beta.h … @implementation Beta … @end #import Gamma.h … @implementation Gamma … [self doSomething]; … @end Alpha.h Beta.h Gamma.h Alpha.m Beta.m Gamma.m
  75. 75. クラス定義とヘッダファイル #import <Foundation/NSobject.h> @interface Alpha : NSObject { … } - (void)doSomething; @end #import Alpha.h @interface Beta : Alpha { … } … @end #import Beta.h @interface Gammma : Beta { … } … @end #import Alpha.h … @implementation Alpha … - (void)doSomething { … } … @end #import Beta.h … @implementation Beta … @end #import Gamma.h … @implementation Gamma … [self doSomething]; … @end Alpha.h Beta.h Gamma.h Alpha.m Beta.m Gamma.m 図のGamma.mではメソッド定義内で doSomethingというメソッドを呼びだし ている これはクラスAlphaから継承された もの
  76. 76. クラス定義とヘッダファイル #import <Foundation/NSobject.h> @interface Alpha : NSObject { … } - (void)doSomething; @end #import Alpha.h @interface Beta : Alpha { … } … @end #import Beta.h @interface Gammma : Beta { … } … @end #import Alpha.h … @implementation Alpha … - (void)doSomething { … } … @end #import Beta.h … @implementation Beta … @end #import Gamma.h … @implementation Gamma … [self doSomething]; … @end Alpha.h Beta.h Gamma.h Alpha.m Beta.m Gamma.m Gamma.mがインポートしている Gamma.hはBeta.hをインポートしている Beta.hはAlpha.hをインポートしている為 メソッドdoSomethingの宣言を 参照できる
  77. 77. クラス定義とヘッダファイル #import <Foundation/NSobject.h> @interface Alpha : NSObject { … } - (void)doSomething; @end #import Alpha.h @interface Beta : Alpha { … } … @end #import Beta.h @interface Gammma : Beta { … } … @end #import Alpha.h … @implementation Alpha … - (void)doSomething { … } … @end #import Beta.h … @implementation Beta … @end #import Gamma.h … @implementation Gamma … [self doSomething]; … @end Alpha.h Beta.h Gamma.h Alpha.m Beta.m Gamma.m クラス定義ではいくらでも継承関係を繰 り返す事が出来る しかし,このインポート方法を守ってい る限りクラス階層の下位のどのクラス 定義からも,上位クラスのインタフェー スが見える事になる
  78. 78. 継承を用いたクラス定義 継承とメソッド呼び出し サブクラス内のメソッド 新たに追加したインスタンス変数に自由にアクセスできる スーパークラスで定義されているインスタンス変数にもアクセスできる スーパークラスで定義したメッセージは継承される 元々存在していたかのように呼び出して利用できる
  79. 79. 継承とメソッド呼び出し 変数x method1 method2 method3 変数x method1 method2 method3 変数x method1 method2 method3 継承 継承 変数x 変数x インスタンス化 インスタンス化 クラスA クラスCクラスB 図3-4 継承とメソッド
  80. 80. 継承とメソッド呼び出し 変数x method1 method2 method3 変数x method1 method2 method3 変数x method1 method2 method3 継承 継承 変数x 変数x インスタンス化 インスタンス化 クラスA クラスCクラスB method1,method2,method3を持つクラスAをクラスBが継承し, method2を定義し直したとする さらにクラスBをクラスCが継承し,method1を定義し直したとする
  81. 81. 継承とメソッド呼び出し 変数x method1 method2 method3 変数x method1 method2 method3 変数x method1 method2 method3 継承 継承 変数x 変数x インスタンス化 インスタンス化 クラスA クラスCクラスB クラスBのインスタンスに対してメッセージが送られた場合を考える まずmethod1に対応するメッセージが送信された(メソッドの呼び出しが起きた) method1の定義はクラスBには無いが,継承されているクラスAの定義が 用いられるので問題無い
  82. 82. 継承とメソッド呼び出し 変数x method1 method2 method3 変数x method1 method2 method3 変数x method1 method2 method3 継承 継承 変数x 変数x インスタンス化 インスタンス化 クラスA クラスCクラスB method3の場合もmethod1と同様 method2はクラスBに定義があるので,呼び出しが起きたときはその定義が使われる
  83. 83. 継承とメソッド呼び出し 変数x method1 method2 method3 変数x method1 method2 method3 変数x method1 method2 method3 継承 継承 変数x 変数x インスタンス化 インスタンス化 クラスA クラスCクラスB クラスCにインスタンスメッセージが送られた場合 method1の定義はクラスCにあるのでこの定義が使われる method2は定義がクラスCにはないので,呼び出された場合は クラスBの定義が使われる
  84. 84. 継承とメソッド呼び出し 変数x method1 method2 method3 変数x method1 method2 method3 変数x method1 method2 method3 継承 継承 変数x 変数x インスタンス化 インスタンス化 クラスA クラスCクラスB method3は定義がクラスCにもクラスBにも無いので,クラスAの定義が使われる
  85. 85. 継承を用いたクラス定義 スーパークラスのメソッドの呼び出し 図3-4のクラスBのmethod2の定義内でクラスAのmethod2を呼び出したい場合 selfに対してmethod2の呼び出しを行うと,自分自身のmethod2の定義が 使われてしまう 再起呼び出しになってしまう
  86. 86. スーパークラスのメソッドの呼び出し スーパークラスのメソッドをサブクラスで利用したい場合superという 特別な名前に対してメッセージを送るように記述する 自分の持っているメソッドではなく,スーパークラス,あるいはさらに継承の 上位のクラスで定義されているメソッドが呼び出せる method1 method2 method3 method1 method2 [super method2]; method3 method1 [super method3]; method2 method3 継承 継承 クラスA クラスCクラスB
  87. 87. スーパークラスのメソッドの呼び出し 図ではクラスCでmethod1とmethod3が定義されている クラスCのmethod1の定義内でsuperに対してmethod3の呼び出しを行うと クラスCではなくクラスAのメソッドが呼び出される method1 method2 method3 method1 method2 [super method2]; method3 method1 [super method3]; method2 method3 継承 継承 クラスA クラスCクラスB
  88. 88. スーパークラスのメソッドの呼び出し 図ではクラスCでmethod1とmethod3が定義されている クラスCのmethod1の定義内でsuperに対してmethod3の呼び出しを行うと クラスCではなくクラスAのメソッドが呼び出される method1 method2 method3 method1 method2 [super method2]; method3 method1 [super method3]; method2 method3 継承 継承 クラスA クラスCクラスB superはスーパークラスのメソッドを 呼び出すという目的以外には使えない
  89. 89. 継承を用いたクラス定義 イニシャライザの定義 新たにインスタンスを変数を追加して初期化したい スーパークラスでの初期化とは異なる方法で初期化を行いたい 上記のような場合サブクラスでイニシャライザを定義する
  90. 90. イニシャライザの定義 例えばinitをサブクラスで上書きする場合,通常は以下のような書き方をする 他のイニシャライザも同様 - (id)init { self = [super init]; /* 必ず最初にスーパークラスのイニシャライザを呼ぶ */ if (self != nil) { /* スーパークラスからインスタンスが返されたら */ … /* ここにサブクラスに固有の初期化コードを書く */ } return self; }
  91. 91. イニシャライザの定義 最初にスーパークラスのinitを使っている これによりスーパークラスで定義されているインスタンス変数などの初期化を 行う事が出来るので,その下にサブクラスに固有の初期化のコードを書けばよい - (id)init { self = [super init]; /* 必ず最初にスーパークラスのイニシャライザを呼ぶ */ if (self != nil) { /* スーパークラスからインスタンスが返されたら */ … /* ここにサブクラスに固有の初期化コードを書く */ } return self; }
  92. 92. イニシャライザの定義 すべてのクラス定義において,この方法を使って初期化することに決めてけば 必ずルートクラスNSObjectのinitを実行できる この初期かメソッドを実行しないと,オブジェクトとして振る舞う事が出来ない 同時に,あるスーパークラスで定義されているインスタンス変数を初期化し損なう といった事を防止できる
  93. 93. イニシャライザの定義 スーパークラスによってはイニシャライザが初期化に失敗する場合がある その場合オブジェクトを何も表さない無効な値であるnilが返されるので サブクラス側でも初期化せずにnilをそのままメソッドの返り値とする
  94. 94. 継承によるプログラム例 メソッド追加の例 例として,値をすぐに最小値に設定できるようなミューと機能付きの新しいクラス MuteVolumeを定義してみる まず単純にメッセージmuteを受け取ると,ボリューム値が最小になるというだけの 機能をつけてみる
  95. 95. メソッド追加の例 List3-1 MuteVolume.h - バーション1 List3-2 MuteVolume.m - バーション1
  96. 96. メソッド追加の例 List3-3 MuteVolumeをテストするmain.m このmainプログラムは端末から文字列を読み 込み,先頭文字がu,d,mのどれであるかで ボリュームの値を変化させる qが先頭文字のとき終了する % cc main.m Volume.m MuteVolume.m -framework Foundation
  97. 97. コンパイルは以下のように行う 実行結果は以下のようになる % cc main.m Volume.m MuteVolume.m -framework Foundation
  98. 98. 継承によるプログラムの例 メソッドの上書きの例 もう少し実用的な使用にしてみる もう一度メッセージmuteを送ると元に戻るようにする ミュート機能が働いている間にupやdownのメッセージが送られてきたら,ボリュームの 値としては最小値を返すが,内部的にはボリュームの値を変化させるようにする
  99. 99. メソッドの上書きの例 List3-4MuteVolume.h - バーション2 ミュート状態にあるかどうかを示す BOOL型のインスタンス変数mutingを 追加して,メソッドinitWithMin:max:stepと メソッドvalueを変更することにする
  100. 100. メソッドの上書きの例 List3-5 MuteVolume.m - バージョン2
  101. 101. メソッドの上書きの例 実行結果は右のようになる
  102. 102. 継承とメソッド呼び出し selfを使ったメソッド呼び出し メソッドの定義の中で,自分のクラス内のメソッドを呼びだす場合selfを使う 継承を用いたクラス定義ではselfに対するメッセージ呼び出しには注意が必要
  103. 103. selfを使ったメソッド呼び出し method1 method2 method3 method1 method2 method3 [self method1]; [self method2]; method1 method2 method3 継承 継承クラスA クラスB クラスC インスタンス化 インスタンス化 メッセージ送信 メッセージ送信
  104. 104. method1 method2 method3 method1 method2 method3 [self method1]; [self method2]; method1 method2 method3 継承 継承クラスA クラスB クラスC インスタンス化 インスタンス化 メッセージ送信 メッセージ送信 selfを使ったメソッド呼び出し 今クラスBでmethod3の定義の中からmethod1とmethod2を呼び出して使いたい selfに対してmethod1と2の呼び出しを行うようにしたとする クラスのインスタンスに対してmethod3の呼び出しを行う まずselfに対してmethod1が送られクラスBで定義した method1が実行される 次にselfに対してmethod2が送られるが,クラスBにはmethod2の定義は無い ので,クラスAから継承した定義を使う
  105. 105. method1 method2 method3 method1 method2 method3 [self method1]; [self method2]; method1 method2 method3 継承 継承クラスA クラスB クラスC インスタンス化 インスタンス化 メッセージ送信 メッセージ送信 selfを使ったメソッド呼び出し クラスCに対してmethod3の呼び出しを行った場合 クラスCにはmethod3の定義が無いのでBから継承したmethod3が実行される
  106. 106. selfを使ったメソッド呼び出し selfはメッセージ処理を行っているインスタンス自身の事を示している 同じプログラムを実行しても,実際に呼び出されるメソッドはインスタンスの クラスによって異なる事がある method1 method2 method3 method1 method2 method3 [self method1]; [self method2]; method1 method2 method3 継承 継承クラスA クラスB クラスC インスタンス化 インスタンス化 メッセージ送信 メッセージ送信
  107. 107. superを使ったメソッドの呼び出し method1 method2 method3 method1 method2 method3 [super method1]; [super method2]; method1 method2 method3 継承 継承クラスA クラスB クラスC
  108. 108. superを使ったメソッドの呼び出し selfを使う代わりにsuperを使う場合 クラスBのメソッドの定義でsuperと書いた時点で,そのメソッドはクラスBが スーパークラスから継承したものと決まってしまう クラスB,クラスCのどちらのインスタンスがmethod3を実行しても, 呼び出されるのはクラスAで定義されたmethod1とmethod2
  109. 109. superを使ったメソッドの呼び出し このようにsuperはsuperを使っているクラスが継承したメソッドを使う事を表すので どのメソッドが実行されるのかはコンパイル時にクラスの継承関係から決定される method1 method2 method3 method1 method2 method3 [super method1]; [super method2]; method1 method2 method3 継承 継承クラスA クラスB クラスC
  110. 110. 実験プログラム メッセージ送信の仕組みを調べるために 簡単なプログラムtestself.mを作成する このプログラムにはクラスA,B,Cがあり クラスAにはメソッド method1,method2が 定義されている
  111. 111. 実験プログラム クラスBではmethod2を上書きし selfに対するmethod1の呼び出しと superに対するmethod2の呼び出しを 行うようにしている
  112. 112. 実験プログラム クラスCはmethod1を上書きしている
  113. 113. 実験プログラム 実行結果は以下のようになる クラスBのインスタンスと,クラスCのインスタンスの場合で, 呼び出されるメソッドが異なる事がわかる
  114. 114. おわり

×