SlideShare une entreprise Scribd logo
1  sur  114
Télécharger pour lire hors ligne
第10回iPhone輪講
CHAPTER02
CHAPTER02
Objective-Cのプログラム
オブジェクトとメッセージ
メッセージ式
オブジェクトはどのクラスに属するものでもidという特別な型で表現される
オブジェクトを格納するための変数objの宣言は以下のようになる
あるオブジェクトにobjにメッセージmsgを送る事を
のように記述しメッセージ式(message expression)と呼ぶ
id obj;
[ obj msg ]
オブジェクトとメッセージ
メッセージ式は,レシーバであるオブジェクトがそのメッセージを処理した結果の値を返す
メッセージ式の扱いはCの関数呼び出しと同じ
メッセージ式は別の式の構造要素としても使う事ができる
void型を返すメッセージ式もある
オブジェクトとメッセージ
メッセージの送り先(objと書いた部分)には,オブジェクトを表す式も記述できる
従って,あるメッセージ式の結果がオブジェクトなら,以下のように
さらに続けてメッセージを送る事が出来る
Cでは[ ]は配列の要素を参照する演算子だが,Objective-Cではメッセージ式を
表すために使っている
配列の演算子かどうかは,左側に配列名やポインタなど別の式の有無で
区別できる
[[ obj msg1 ] msg2];
[[ obj msg1 ] msg2 ] msg3 ];
オブジェクトとメッセージ
メッセージ式の値をそのまま配列の添字として使う場合は以下のようになる
メッセージは変数名のような識別子と同様なルールで作られたメッセージキーワード
から構成されている
関数呼び出しと同様にメッセージには引数を付ける事もできる
引数の無いメッセージは,1つのメッセージキーワードだけからなる
element = table[[ obj count ]];
オブジェクトとメッセージ
以下に例を挙げる
2つ目の例にあるように,変数名とメッセージキーワードは名前が重複していても構わない
メッセージ式の中ではメッセージキーワードかどうか区別できる
[aString copy];
width = [node width];
[[doc filename] retain;
オブジェクトとメッセージ
メッセージに引数がある場合はメッセージキーワードの末尾に「:」を付けて引数が
ある事を示す
「:」の次には実引数がくる.実引数は関数呼び出しと同様に一般の式が記述できる
別のメッセージ式を実引数とする事も出来る
[printInfo setLeftMargin: 60.0];
[[[cw window] firstResponder] copy: sender;
[doc isSameDirectory:[info objectAtIndex: ++num]];
オブジェクトとメッセージ
2つ以上の引数がある場合は「:」を付けた別のキーワードを追加する
あるいはキーワードなしで「:」だけを追加することも文法上は許されている
逆に「:」が付かないキーワードを追加する事は無い
[cell = [albumview cellAtRow:i column: j];
[manager fileExistsAtPath:dirname isDirectory:&isdir];
[view lineTo: 1.4142 : (y + 1.0)];
オブジェクトとメッセージ
メッセージセレクタ
関数を関数名で呼ぶように,個々のメッセージはキーワードを書き並べたものを名前として
表し,他のメッセージと区別する
これをメッセージセレクタ(message slector),あるいはセレクタ(selector)と呼ぶ
メソッド名と呼ぶ事もある
メッセージセレクタ
メッセージ式の例にあったいくつかのメッセージは以下のようなセレクタを持つ
引数を持つキーワードは「:」もセレクタに含める事に注意する
以下の例ではcopyとcopy:は異なるセレクタになる
copy
retain
firstResponder
copy:
objectAtIndex:
cellAtRow:column:
fileExistsAtPath:isDirectory:
lineTo:
Objective-Cのプログラム
インスタンスの生成と初期化
id型の変数を宣言しただけでは,その変数には何のオブジェクトも格納されていない
オブジェクトに仕事をしてもらうには,まずクラスからインスタンスを生成する事が
必要となる
Objective-Cではクラスにメッセージを送る事でインスタンスの生成を行う
インスタンスの生成と初期化
クラス名に対してメッセージallocを送る事により,新しいインスタンスが1つ作成される
このように生成されたインスタンスは,メモリ上に必要な領域が確保されただけ
通常はこの直後に初期化を行う必要がある
初期化のためのメソッドはイニシャライザ(initializer)と呼ぶ
イニシャライザとしてどのようなメソッド用いるかは各クラスによって異なる
[クラス名 alloc]
インスタンスの生成と初期化
Cocoaでは通常,イニシャライザはinitか,initから始まるメソッド名を持つものである
という約束がある
あるクラスからインスタンスを作成するメッセージ式の典型例
インスタンスの生成は常にallocのメッセージ式に直接イニシャライザを
適用する形式で書く
[[ クラス名 alloc ] init ]
インスタンスの生成と初期化
初期化はインスタンスの情報を「リセット」するものではない
イニシャライザによる初期化はそのインスタンスが生成された直後に一度だけ行う
リセットに相当する処理(インスタンス変数の値を初期値に戻す)が必要な場合は
イニシャライザとは別なメソッドとして実現する
クラス定義
クラスのインタフェース部
インタフェース部にはそのクラスのインスタンンス変数
とメソッドを宣言する
通常はヘッダファイルとして作成し,そのクラスを使
うモジュールから参照できるようにする
@interface クラス名 : スーパークラス名;
{
インスタンス変数の宣言;
...
}
メソッドの宣言;
...
@end
クラスのインタフェース部
@interfaceと@endはObjective-Cで導入された
コンパイラ指示子と呼ばれる識別子
インタフェースの始まりと終わりを表している
他にもコンパイラ指示子はいくつかある
すべて「@」で始まり,従来のC言語の部分と
区別できるようになっている
@interface クラス名 : スーパークラス名;
{
インスタンス変数の宣言;
...
}
メソッドの宣言;
...
@end
クラスのインタフェース部
クラス名にはC言語などと同様のルールに従う識別子を
用いる
先頭を大文字にした単語を並べたものをクラス名と
する習慣がある
クラス名は変数名や関数名と重なってはいけない
@interface クラス名 : スーパークラス名;
{
インスタンス変数の宣言;
...
}
メソッドの宣言;
...
@end
スーパークラスについては「継承の概念」で説明する
今の段階では「NSObject」と書く事にしておく
@interface クラス名 : スーパークラス名;
{
インスタンス変数の宣言;
...
}
メソッドの宣言;
...
@end
クラスのインタフェース部
インスタンス変数の宣言はC言語の変数定義と同様に
「型�変数名」と記述しておく
id型のインスタンスも使える
この変数名はクラス内のあらゆるメソッドから
参照されるのでaaaのような単純なもの,意味不
明な名前は避けるようにする
@interface クラス名 : スーパークラス名;
{
インスタンス変数の宣言;
...
}
メソッドの宣言;
...
@end
クラスのインタフェース部
メソッドの宣言は,C言語の関数のプロトタイプ宣言に
相当するもの
まず引数が無く,返り値にオブジェクトを返すメソッド
の宣言は以下のように書く
返り値の型は()で囲んだ型名をメソッドの前に置く
ここではメソッド名をdelegateだとする
@interface クラス名 : スーパークラス名;
{
インスタンス変数の宣言;
...
}
メソッドの宣言;
...
@end
- (id)delegate;
クラスのインタフェース部
仮引数の型の指定も同様に()で囲んだ型名を仮引数
の前に置く
2つの整数を引数とし,返り値としてオブジェクトを返す
メソッドcellAtRow:column:の宣言は以下のようになる
rowとcolは仮引数
値を返さないメソッドにはCと同様にvoid型を指定する
@interface クラス名 : スーパークラス名;
{
インスタンス変数の宣言;
...
}
メソッドの宣言;
...
@end
- (id)cellAtRow:(int)row column:(int)col;
- (void)setAutodisplay:(BOOL)flag;
クラスのインタフェース部
メソッドの宣言は,C言語の関数のプロトタイプ宣言に
相当するもの
まず引数が無く,返り値にオブジェクトを返すメソッド
の宣言は以下のように書く
返り値の型は()で囲んだ型名をメソッドの前に置く
ここではメソッド名をdelegateだとする
@interface クラス名 : スーパークラス名;
{
インスタンス変数の宣言;
...
}
メソッドの宣言;
...
@end
- (id)delegate;
クラス定義
クラスの実装部
クラスの実現部分は実装部(インプリメント部)と呼び,
右のように記述する
@implementationは実装部の始まりを示すコンパイラ
指示子です
実装部にはスーパークラス名,インスタンス変数は
記述しない
@implementation クラス名
メソッドの定義
...
@end
クラスの実装部
インタフェース部で宣言したメソッドは実装部で必ず定
義しなければならない
インタフェース部でメソッドを宣言しなかった場合
実装部にメソッドを全く定義しない事も出来る
@implementation クラス名
メソッドの定義
...
@end
クラスの実装部
メソッドの定義は,メソッドの宣言と同じ記述の後に
Cの関数本体の定義と同じ形式のブロックを置いたもの
簡単なメソッドの定義の例は以下
@implementation クラス名
メソッドの定義
...
@end- (double)evalution:(int)val
{
double tmp = [order proposedBalance: val];
if (currentValue > (int)tmp)
tmp = [order proposedBalance: val * 1.25];
return tmp;
}
クラスの実装部
メソッド内からはそのクラスのインスタンス変数に自由にアクセス可能
例ではorderとcurrentValeはインスタンス変数
メソッド内局所変数の定義方法はこの関数と同様
ただしインスタンス変数と同じ名前で定義すると,インスタンス変数が隠されて
アクセスできなくなる
メソドの仮引数も同様でインスタンス変数と同じ名前にするのを避ける
クラスの実装部
メソッド定義の中で,そのメソッドを使っているインスタンスオブジェクト自身を表している場
合はselfという名前を使う
selfは変数に代入したり,メソッドの返り値として返す事も出来る
メソッドの再起呼び出しも可能
クラス定義
クラス定義の例
リモコンでコントロールできるテレビなどのボリュームをシミュレートする
クラスVolumeを考える
このクラスには最小値,最大値,および変化の刻み幅を初期設定するメソッド,
値を1レベル上げるメソッド,下げるメソッドがある
各インスタンスは音量として,最小値から最大値までの間どれかの値を持ち,
volumeというメソッドでその値を調べる事が出来る
クラス定義の例
List2-1 Volumeクラスのインタフェース部
List2-2 Volumeクラスの実装部
クラス定義の例
[super init]はスーパークラスであるNSObjctの初期メッセージの呼び出しを
表している
nilはオブジェクトとして無効な値であることを示す定数として定められている
スーパークラスからの返り値が無効なら初期化は行わない
クラス定義の例
initWithMin:max:step,up,downの3つのメソッドが返り値としてselfを返している事に
注意する
selfはメッセージを受け取って,そのメソッドの処理をしているインスタンスオブジェクト
そのもの
返り値に対して続けてメッセージを送る事が出来る
変数objにVolumeクラスのインスタンスが格納されているとすると,
以下のような式が実行できる
[[[obj up] up] up]
クラス定義の例
クラスの定義だけしても,プログラムとしては動作しない
Objective-CもC言語と同様に関数main()が必要
以下List2-3 Volumeクラスをテストするmain関数
コンパイル
簡単なコンパイル方法
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:簡単なファイルの構成
簡単なコンパイル方法
Cocoaではシステムが提供するクラスや関数はフレームワーク(framework)という
構造を持った動的なライブラリとして提供される
ここではFoundationというフレームワークを使うので,コンパイルオプションとして
このフレームワークを使うということを指示しなくてはならない
コンパイルは次のように行う
-frameworkというオプションがフレームワークの指定
% cc voltest.m -framework Foundation
簡単なコンパイル方法
これで実行ファイルとしてa.outが作成される
ソースファイルがCかC++かObjective-Cなのかは拡張子で区別できる
なので特に細かい指定をする必要が無い
実行ファイル名を指定する場合は「-o」オプションを使う
コンパイル時警告を表示させたいときは「-Wmost」オプション
または「-Wall」オプションを使う
実行結果は以下のようになる
コンパイル
分割コンパイル
Objective-Cでは各クラスごとに別々のファイルとして作成するのが普通
クラスを1つ作成するごとに,以下の2つのファイルを作成する
1.インタフェースファイル
インタフェース部を記述
ファイル名は「クラス名.h」
2.実装ファイル
実装部を記述
ファイル名は「クラス名.m」
分割コンパイル
インタフェース部はヘッダファイルになる
そのクラスを使う別のソースファイルで,そのヘッダファルをインポートする
実装ファイルでもインタフェースの情報を知らなければならないので,
インタフェースファイルは必ずインポートする
インタフェースを定義するにはスーパークラスで指定したクラスについての情報が必要
例題のプログラムの場合「Foundation/NSObject」というファイルを
インタフェースファイル内でインポートしなければならない
分割コンパイル
先ほどのmain関数にはクラス定義は含まれていないが,関数内でメッセージ式を
使っている
メッセージ式はObjective-C特有のもの
main関数を含むファイルをC言語のプログラムとしてコンパイルすることは
出来ない
クラス定義が含まれていなくても,クラスを参照したり,メッセージ式を使っている場合
そのソースファイルの拡張子は「.m」にしておく必要がある
ここではmain関数を含むファイルの名前をmain.mとする
分割コンパイル
これらのコンパイルは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
プログラムの書き方について
ハイブリッド言語
Objcetive-CはC言語にオブジェクト指向を取り入れた言語
完全にオブジェクト指向風に記述する事が出来る
従来通りCの記述とオブジェクトを混在させる事も出来る
このような性質からObjective-Cはハイブリッド言語と呼ばれることもある
ハイブリッド言語
Objective-CのプログラムにC言語の関数を組み合わせるのは以下のような
場合が考えられる
すでに安定して動作している関数モジュールをそのまま使いたい場合
UnixのシステムコールなどC言語で記述されたインタフェースを使う場合
特定のオブジェクトに関係しない数学用,計算用ルーチンである場合
クラス定義で他のメソッドの下請けとして使う場合
実行速度が特に問題となる場合
ハイブリッド言語
Objective-Cではソフトウェア全体の枠組みはオブジェクト指向で記述し,
必要とされる部分をCの関数で記述するという自由度の高いプログラミングを
行う事が出来る
ハイブリッド言語
Objective-Cでは関数の中から,メソッド定義の中からも自由にCの関数を呼び出す事が
出来る
関数の中からメソッドを呼びだす事が出来る
引数や返り値がid型であるような関数も作成できる
Cの関数定義のみを含むソースファイルは通常のCのプログラム同様,Cを拡張子とする
ファイルとして作成する
ただし関数の中でメソッドを呼びだしたり,クラス定義に関する情報を
使用したりする場合はCのソースファイルとしてコンパイルできないので
拡張子を「.m」としておく必要がある
同時に対応するクラスのインタフェースを含むヘッダファイルをインポートしなければ
ならない
ハイブリッド言語
メソッド定義内から関数を呼び出す場合には,その関数に関する情報
つまりプロトタイプ宣言が参照できていれば十分
拡張子が「.c」であるファイルと「.m」であるファイルは問題なく一緒に
コンパイル,リンクできる
例えば以下のようにして実行ファイルを作成できる
% cc -o sample main2.m util.c Volume.m -framework Foundation
ハイブリッド言語
クラス実装ファイル内に関数定義を記述する
事も出来る
ただしメソッドの定義の下請け関数など
そのクラスの実装に直接関係のあるもの
に限定すべき
関数の定義は@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:実行ファイル内の関数定義
ハイブリッド言語
実装ファイル内で局所的な関数なので
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:実行ファイル内の関数定義
ハイブリッド言語
実装ファイル内で定義した関数であっても
その関数の内部からインスタンス変数や
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:実行ファイル内の関数定義
ハイブリッド言語
そのクラス全体で共通する機能や手続きは
関数として定義するよりも,
クラスメソッドとして定義する方がふさわしい
事がある
#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:実行ファイル内の関数定義
プログラムの書き方について
静的な変数定義
関数やメソッドの定義外で定義された変数および,static指示子を付けて定義した変数は
静的な変数(プログラム実行開始から終了まで存在し続ける変数)になる
メソッドの定義内でもstatic付きの静的な変数を利用することはできるが,
通常のCプログラミングとは違うので注意
静的な変数定数
インスタンスオブジェクトは複数個作られる可能性があるが,静的変数の宣言は
1ヶ所にしかない
従って静的な変数は複数個のインスタンスオブジェクトで共有されることになる
あるインスタンスが値を格納して,次にそれを参照するまでの間に別のインスタンスが
値を変更してしまうという事も起こる
プログラムの書き方について
ヘッダファイルのインポート
Objective-Cでは#importというプロセッサ制御行を使う
これは#includeと同じだが,#importは一度読み込んだヘッダファイルを
複数回インクルードしないという特徴がある
継承の概念
スーパークラスとサブクラス
あるクラスを定義するとき,そのクラスが既に定義されている別のクラスの機能を
拡張したり,一部を変更したりしたものである場合もよくある
既に存在する別のクラスの定義を引き継いで新しいクラスを定義できれば
必要な部分だけ書き足せばよいので簡単に定義が行える
このように別のクラスの定義の一部を拡張あるいは変更して新しいクラスを
定義する機能を継承(inheritance)という
CHAPTER03
継承とクラス
スーパークラスとサブクラス
新しく作るクラスから見て継承の元となるクラスをスーパークラス(superclass)
逆に継承の元となるクラスから見て新しく作るクラスをサブクラス(subclass)と呼ぶ
サブクラスは
スーパークラスが持っていた定義をすべて引き継ぐ
スーパークラスが持っていたインスタンス変数を持つ
スーパークラスと同じメソッドが実行できる
スーパークラスとサブクラス
サブクラスはさらに
新しいメソッドを追加できる
新しいインスタンス変数を追加できる
スーパークラスのメソッドを別の定義で置き換えられる
という変更が加えることが出来る
ただし,メソッドの変更なしに新しいインスタンス変数の追加だけ行っても
意味が無い
スーパークラスのメソッドを新しい定義で置き換える事を上書き(override)と呼ぶ
スーパークラスとサブクラス
クラスBはクラスAを継承し,
同じインスタンス変数とメソッドを
備えているがmethod2は別の定義で
置き換えられている
変数x
method1
method2
変数x
method1
method2
変数x
method3
method2
method1
クラスA
クラスC
クラスB
変数z
継承
継承
図3-1
継承の概念
スーパークラスとサブクラス
クラスCもクラスAを継承しているが
新たに変数zとmethod3が付け加え
られている
この3つのクラスのインスタンスはどれも
クラスAのインタフェースに記述された
method1,method2に対応できる事に
注意
変数x
method1
method2
変数x
method1
method2
変数x
method3
method2
method1
クラスA
クラスC
クラスB
変数z
継承
継承
図3-1
継承の概念
継承の概念
クラス階層
ある有用なクラスがあると,それを継承して新しいクラスを作ったり,
継承を使って定義したクラスをさらに継承して別のクラスを作ったりと言う事を
繰り返す事がある
結果スーパークラス,サブクラスの関係が木のような階層関係に成長する事がある
これをクラス階層(class hierarchy)と呼ぶ
クラス階層
クラス階層をさかのぼると,それ以上スーパークラスを持たないクラスに行き着く
これをルートクラス(root class)と呼ぶ
CocoaにはルートクラスとしてNSObjectが用意されている
他のクラスは直接,あるいは間接にNSObjectを継承する
自分でクラスを作る場合もNSObjectあるいは既存のクラスをサブクラスとして
定義しなければならない
クラス階層
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
クラス階層の概念
継承を用いたクラス定義
継承関係の宣言
あるクラスを継承して,サブクラスを新たに
定義したい場合
Objective-Cではサブクラスの
インタフェース部で継承関係を
宣言する
@interface クラス名 : スーパークラス名
{
インスタンス変数の宣言;
…
}
   メソッドの宣言;
…
@end
継承関係の宣言
あるクラスAのサブクラスBを新たに定義
する場合
クラス名に新しいクラスの名前としてB
スーパークラス名に継承したい
スーパークラスの名前であるAを書く
B A
@interface クラス名 : スーパークラス名
{
インスタンス変数の宣言;
…
}
   メソッドの宣言;
…
@end
継承関係の宣言
Objective-Cではすべてのクラスはルートクラスを継承しなければ
オブジェクトとして振る舞う事が出来ない
継承しているクラスがはっきりしている場合には,そのクラス名をスーパークラス名として
記述すればよい
特に継承すべきクラスが無い場合,NSObjectクラスをスーパークラスとして
指定しなければならない
継承関係の宣言
「インスタンス変数の宣言」にはサブクラスで
新たに付け加えるインスタンス変数のみを
記述する
追加するインスタンス変数がない場合
{}の対だけを書くか,あるいはそれも
省略する
@interface クラス名 : スーパークラス名
{
インスタンス変数の宣言;
…
}
   メソッドの宣言;
…
@end
継承関係の宣言
「メソッドの宣言」もサブクラスで追加するも
のだけ記述すればよい
スーパークラスで定義されているメソッド
をサブクラスで変更(上書き)したい場合
インタフェース部にも改めて記述し,
変更されている事を表すコメントを付けて
おくとわかりやすい
@interface クラス名 : スーパークラス名
{
インスタンス変数の宣言;
…
}
   メソッドの宣言;
…
@end
継承関係の宣言
図3-1のクラスAを継承したクラスBを定義する場合のインタフェース部は
変数x,メソッドmthod1はクラスAの宣言を継承するので記述しない
メソッドmethod2も記述しなくてよい
@interface B : A
- (void)method2; //このメソッドは上書き
@end
継承関係の宣言
クラスCを定義する場合のインタフェース部は
変数z,method3の宣言を記述しなければならない
@interface C : A
{
id z;
}
- (void)method3;
@end
継承を用いたクラス定義
クラス定義とヘッダファイル
分割コンパイラの説明で
「インタフェース部はひとつのヘッダファイルとして作成するのが普通」とあった
継承にとってもこの事は重要
クラス定義とヘッダファイル
あるクラスAlphaが既に定義されていたとする
Alpha.hというヘッダファイルが存在する
このクラスAlphaを継承してBetaを定義する場合
Beta.hでAlpha.hを継承しなければならない
クラス定義とヘッダファイル
スーパークラス名で指定されたクラスがどんなものかという情報が無くては
サブクラスの定義が出来ない
このためにスーパークラスのインタフェース部を含むヘッダファイルが必要になる
クラス定義とヘッダファイル
#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
クラス定義とヘッダファイル
#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から継承された
もの
クラス定義とヘッダファイル
#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の宣言を
参照できる
クラス定義とヘッダファイル
#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
クラス定義ではいくらでも継承関係を繰
り返す事が出来る
しかし,このインポート方法を守ってい
る限りクラス階層の下位のどのクラス
定義からも,上位クラスのインタフェー
スが見える事になる
継承を用いたクラス定義
継承とメソッド呼び出し
サブクラス内のメソッド
新たに追加したインスタンス変数に自由にアクセスできる
スーパークラスで定義されているインスタンス変数にもアクセスできる
スーパークラスで定義したメッセージは継承される
元々存在していたかのように呼び出して利用できる
継承とメソッド呼び出し
変数x
method1
method2
method3
変数x
method1
method2
method3
変数x
method1
method2
method3
継承 継承
変数x 変数x
インスタンス化 インスタンス化
クラスA クラスCクラスB
図3-4
継承とメソッド
継承とメソッド呼び出し
変数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を定義し直したとする
継承とメソッド呼び出し
変数x
method1
method2
method3
変数x
method1
method2
method3
変数x
method1
method2
method3
継承 継承
変数x 変数x
インスタンス化 インスタンス化
クラスA クラスCクラスB
クラスBのインスタンスに対してメッセージが送られた場合を考える
まずmethod1に対応するメッセージが送信された(メソッドの呼び出しが起きた)
method1の定義はクラスBには無いが,継承されているクラスAの定義が
用いられるので問題無い
継承とメソッド呼び出し
変数x
method1
method2
method3
変数x
method1
method2
method3
変数x
method1
method2
method3
継承 継承
変数x 変数x
インスタンス化 インスタンス化
クラスA クラスCクラスB
method3の場合もmethod1と同様
method2はクラスBに定義があるので,呼び出しが起きたときはその定義が使われる
継承とメソッド呼び出し
変数x
method1
method2
method3
変数x
method1
method2
method3
変数x
method1
method2
method3
継承 継承
変数x 変数x
インスタンス化 インスタンス化
クラスA クラスCクラスB
クラスCにインスタンスメッセージが送られた場合
method1の定義はクラスCにあるのでこの定義が使われる
method2は定義がクラスCにはないので,呼び出された場合は
クラスBの定義が使われる
継承とメソッド呼び出し
変数x
method1
method2
method3
変数x
method1
method2
method3
変数x
method1
method2
method3
継承 継承
変数x 変数x
インスタンス化 インスタンス化
クラスA クラスCクラスB
method3は定義がクラスCにもクラスBにも無いので,クラスAの定義が使われる
継承を用いたクラス定義
スーパークラスのメソッドの呼び出し
図3-4のクラスBのmethod2の定義内でクラスAのmethod2を呼び出したい場合
selfに対してmethod2の呼び出しを行うと,自分自身のmethod2の定義が
使われてしまう
再起呼び出しになってしまう
スーパークラスのメソッドの呼び出し
スーパークラスのメソッドをサブクラスで利用したい場合superという
特別な名前に対してメッセージを送るように記述する
自分の持っているメソッドではなく,スーパークラス,あるいはさらに継承の
上位のクラスで定義されているメソッドが呼び出せる
method1
method2
method3
method1
method2
[super method2];
method3
method1
[super method3];
method2
method3
継承 継承
クラスA クラスCクラスB
スーパークラスのメソッドの呼び出し
図ではクラス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
スーパークラスのメソッドの呼び出し
図ではクラス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はスーパークラスのメソッドを
呼び出すという目的以外には使えない
継承を用いたクラス定義
イニシャライザの定義
新たにインスタンスを変数を追加して初期化したい
スーパークラスでの初期化とは異なる方法で初期化を行いたい
上記のような場合サブクラスでイニシャライザを定義する
イニシャライザの定義
例えばinitをサブクラスで上書きする場合,通常は以下のような書き方をする
他のイニシャライザも同様
- (id)init
{
self = [super init]; /* 必ず最初にスーパークラスのイニシャライザを呼ぶ */
if (self != nil) { /* スーパークラスからインスタンスが返されたら */
… /* ここにサブクラスに固有の初期化コードを書く */
}
return self;
}
イニシャライザの定義
最初にスーパークラスのinitを使っている
これによりスーパークラスで定義されているインスタンス変数などの初期化を
行う事が出来るので,その下にサブクラスに固有の初期化のコードを書けばよい
- (id)init
{
self = [super init]; /* 必ず最初にスーパークラスのイニシャライザを呼ぶ */
if (self != nil) { /* スーパークラスからインスタンスが返されたら */
… /* ここにサブクラスに固有の初期化コードを書く */
}
return self;
}
イニシャライザの定義
すべてのクラス定義において,この方法を使って初期化することに決めてけば
必ずルートクラスNSObjectのinitを実行できる
この初期かメソッドを実行しないと,オブジェクトとして振る舞う事が出来ない
同時に,あるスーパークラスで定義されているインスタンス変数を初期化し損なう
といった事を防止できる
イニシャライザの定義
スーパークラスによってはイニシャライザが初期化に失敗する場合がある
その場合オブジェクトを何も表さない無効な値であるnilが返されるので
サブクラス側でも初期化せずにnilをそのままメソッドの返り値とする
継承によるプログラム例
メソッド追加の例
例として,値をすぐに最小値に設定できるようなミューと機能付きの新しいクラス
MuteVolumeを定義してみる
まず単純にメッセージmuteを受け取ると,ボリューム値が最小になるというだけの
機能をつけてみる
メソッド追加の例
List3-1 MuteVolume.h - バーション1
List3-2 MuteVolume.m - バーション1
メソッド追加の例
List3-3 MuteVolumeをテストするmain.m
このmainプログラムは端末から文字列を読み
込み,先頭文字がu,d,mのどれであるかで
ボリュームの値を変化させる
qが先頭文字のとき終了する
% cc main.m Volume.m MuteVolume.m -framework Foundation
コンパイルは以下のように行う
実行結果は以下のようになる
% cc main.m Volume.m MuteVolume.m -framework Foundation
継承によるプログラムの例
メソッドの上書きの例
もう少し実用的な使用にしてみる
もう一度メッセージmuteを送ると元に戻るようにする
ミュート機能が働いている間にupやdownのメッセージが送られてきたら,ボリュームの
値としては最小値を返すが,内部的にはボリュームの値を変化させるようにする
メソッドの上書きの例
List3-4MuteVolume.h - バーション2
ミュート状態にあるかどうかを示す
BOOL型のインスタンス変数mutingを
追加して,メソッドinitWithMin:max:stepと
メソッドvalueを変更することにする
メソッドの上書きの例
List3-5 MuteVolume.m - バージョン2
メソッドの上書きの例
実行結果は右のようになる
継承とメソッド呼び出し
selfを使ったメソッド呼び出し
メソッドの定義の中で,自分のクラス内のメソッドを呼びだす場合selfを使う
継承を用いたクラス定義ではselfに対するメッセージ呼び出しには注意が必要
selfを使ったメソッド呼び出し
method1
method2
method3
method1
method2
method3
[self method1];
[self method2];
method1
method2
method3
継承 継承クラスA
クラスB
クラスC
インスタンス化
インスタンス化
メッセージ送信 メッセージ送信
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から継承した定義を使う
method1
method2
method3
method1
method2
method3
[self method1];
[self method2];
method1
method2
method3
継承 継承クラスA
クラスB
クラスC
インスタンス化
インスタンス化
メッセージ送信 メッセージ送信
selfを使ったメソッド呼び出し
クラスCに対してmethod3の呼び出しを行った場合
クラスCにはmethod3の定義が無いのでBから継承したmethod3が実行される
selfを使ったメソッド呼び出し
selfはメッセージ処理を行っているインスタンス自身の事を示している
同じプログラムを実行しても,実際に呼び出されるメソッドはインスタンスの
クラスによって異なる事がある
method1
method2
method3
method1
method2
method3
[self method1];
[self method2];
method1
method2
method3
継承 継承クラスA
クラスB
クラスC
インスタンス化
インスタンス化
メッセージ送信 メッセージ送信
superを使ったメソッドの呼び出し
method1
method2
method3
method1
method2
method3
[super method1];
[super method2];
method1
method2
method3
継承 継承クラスA
クラスB
クラスC
superを使ったメソッドの呼び出し
selfを使う代わりにsuperを使う場合
クラスBのメソッドの定義でsuperと書いた時点で,そのメソッドはクラスBが
スーパークラスから継承したものと決まってしまう
クラスB,クラスCのどちらのインスタンスがmethod3を実行しても,
呼び出されるのはクラスAで定義されたmethod1とmethod2
superを使ったメソッドの呼び出し
このようにsuperはsuperを使っているクラスが継承したメソッドを使う事を表すので
どのメソッドが実行されるのかはコンパイル時にクラスの継承関係から決定される
method1
method2
method3
method1
method2
method3
[super method1];
[super method2];
method1
method2
method3
継承 継承クラスA
クラスB
クラスC
実験プログラム
メッセージ送信の仕組みを調べるために
簡単なプログラムtestself.mを作成する
このプログラムにはクラスA,B,Cがあり
クラスAにはメソッド
method1,method2が
定義されている
実験プログラム
クラスBではmethod2を上書きし
selfに対するmethod1の呼び出しと
superに対するmethod2の呼び出しを
行うようにしている
実験プログラム
クラスCはmethod1を上書きしている
実験プログラム
実行結果は以下のようになる
クラスBのインスタンスと,クラスCのインスタンスの場合で,
呼び出されるメソッドが異なる事がわかる
おわり

Contenu connexe

En vedette

Kotlin/Golang Developer seminor. 「Androidが生み出す開発言語の多様性」 リックテレコム主催
Kotlin/Golang Developer seminor. 「Androidが生み出す開発言語の多様性」 リックテレコム主催Kotlin/Golang Developer seminor. 「Androidが生み出す開発言語の多様性」 リックテレコム主催
Kotlin/Golang Developer seminor. 「Androidが生み出す開発言語の多様性」 リックテレコム主催嶋 是一 (Yoshikazu SHIMA)
 
基礎知識3「ドローンの飛ばし方」
基礎知識3「ドローンの飛ばし方」基礎知識3「ドローンの飛ばし方」
基礎知識3「ドローンの飛ばし方」MapQuest.Inc
 
Scala勉強会 初心者向けハンズオン前編
Scala勉強会 初心者向けハンズオン前編Scala勉強会 初心者向けハンズオン前編
Scala勉強会 初心者向けハンズオン前編takeuchi-tk
 
1-1_C言語入門 - C言語について
1-1_C言語入門 - C言語について1-1_C言語入門 - C言語について
1-1_C言語入門 - C言語についてbc_rikko
 
Scalaで学ぶ関数型言語超入門
Scalaで学ぶ関数型言語超入門Scalaで学ぶ関数型言語超入門
Scalaで学ぶ関数型言語超入門yujiro_t
 
3_C言語入門 - C言語の基本
3_C言語入門 - C言語の基本3_C言語入門 - C言語の基本
3_C言語入門 - C言語の基本bc_rikko
 
Linuxベースのオープンソース フライトコントローラーの概要( #ABC2015S )
Linuxベースのオープンソース フライトコントローラーの概要( #ABC2015S )Linuxベースのオープンソース フライトコントローラーの概要( #ABC2015S )
Linuxベースのオープンソース フライトコントローラーの概要( #ABC2015S )博宣 今村
 
ドローンの仕組み( #ABC2015S )
ドローンの仕組み( #ABC2015S )ドローンの仕組み( #ABC2015S )
ドローンの仕組み( #ABC2015S )博宣 今村
 
基礎知識5「ドローン運用に必要なこと」
基礎知識5「ドローン運用に必要なこと」基礎知識5「ドローン運用に必要なこと」
基礎知識5「ドローン運用に必要なこと」MapQuest.Inc
 
Webアプリケーション負荷試験実践入門
Webアプリケーション負荷試験実践入門Webアプリケーション負荷試験実践入門
Webアプリケーション負荷試験実践入門樽八 仲川
 
偶然にも500万個のSSH公開鍵を手に入れた俺たちは
偶然にも500万個のSSH公開鍵を手に入れた俺たちは偶然にも500万個のSSH公開鍵を手に入れた俺たちは
偶然にも500万個のSSH公開鍵を手に入れた俺たちはYoshio Hanawa
 
Scalaはじめました!
Scalaはじめました!Scalaはじめました!
Scalaはじめました!Asami Abe
 
プログラム組んだら負け!実はHTML/CSSだけでできること2015夏
プログラム組んだら負け!実はHTML/CSSだけでできること2015夏プログラム組んだら負け!実はHTML/CSSだけでできること2015夏
プログラム組んだら負け!実はHTML/CSSだけでできること2015夏Yusuke Hirao
 
Scala超入門 - 2014/12/13 Scala関西勉強会
Scala超入門 - 2014/12/13 Scala関西勉強会Scala超入門 - 2014/12/13 Scala関西勉強会
Scala超入門 - 2014/12/13 Scala関西勉強会Asami Abe
 
中の下のエンジニアを脱出するための仕事術
中の下のエンジニアを脱出するための仕事術中の下のエンジニアを脱出するための仕事術
中の下のエンジニアを脱出するための仕事術Noriaki Kadota
 
ウェブパフォーマンスの基礎とこれから
ウェブパフォーマンスの基礎とこれからウェブパフォーマンスの基礎とこれから
ウェブパフォーマンスの基礎とこれからHiroshi Kawada
 
運用に自動化を求めるのは間違っているだろうか
運用に自動化を求めるのは間違っているだろうか運用に自動化を求めるのは間違っているだろうか
運用に自動化を求めるのは間違っているだろうかMasahito Zembutsu
 
プログラマのための線形代数再入門
プログラマのための線形代数再入門プログラマのための線形代数再入門
プログラマのための線形代数再入門Taketo Sano
 
「スプラトゥーン」リアルタイム画像解析ツール 「IkaLog」の裏側
「スプラトゥーン」リアルタイム画像解析ツール 「IkaLog」の裏側「スプラトゥーン」リアルタイム画像解析ツール 「IkaLog」の裏側
「スプラトゥーン」リアルタイム画像解析ツール 「IkaLog」の裏側Takeshi HASEGAWA
 

En vedette (20)

C言語講習会1
C言語講習会1C言語講習会1
C言語講習会1
 
Kotlin/Golang Developer seminor. 「Androidが生み出す開発言語の多様性」 リックテレコム主催
Kotlin/Golang Developer seminor. 「Androidが生み出す開発言語の多様性」 リックテレコム主催Kotlin/Golang Developer seminor. 「Androidが生み出す開発言語の多様性」 リックテレコム主催
Kotlin/Golang Developer seminor. 「Androidが生み出す開発言語の多様性」 リックテレコム主催
 
基礎知識3「ドローンの飛ばし方」
基礎知識3「ドローンの飛ばし方」基礎知識3「ドローンの飛ばし方」
基礎知識3「ドローンの飛ばし方」
 
Scala勉強会 初心者向けハンズオン前編
Scala勉強会 初心者向けハンズオン前編Scala勉強会 初心者向けハンズオン前編
Scala勉強会 初心者向けハンズオン前編
 
1-1_C言語入門 - C言語について
1-1_C言語入門 - C言語について1-1_C言語入門 - C言語について
1-1_C言語入門 - C言語について
 
Scalaで学ぶ関数型言語超入門
Scalaで学ぶ関数型言語超入門Scalaで学ぶ関数型言語超入門
Scalaで学ぶ関数型言語超入門
 
3_C言語入門 - C言語の基本
3_C言語入門 - C言語の基本3_C言語入門 - C言語の基本
3_C言語入門 - C言語の基本
 
Linuxベースのオープンソース フライトコントローラーの概要( #ABC2015S )
Linuxベースのオープンソース フライトコントローラーの概要( #ABC2015S )Linuxベースのオープンソース フライトコントローラーの概要( #ABC2015S )
Linuxベースのオープンソース フライトコントローラーの概要( #ABC2015S )
 
ドローンの仕組み( #ABC2015S )
ドローンの仕組み( #ABC2015S )ドローンの仕組み( #ABC2015S )
ドローンの仕組み( #ABC2015S )
 
基礎知識5「ドローン運用に必要なこと」
基礎知識5「ドローン運用に必要なこと」基礎知識5「ドローン運用に必要なこと」
基礎知識5「ドローン運用に必要なこと」
 
Webアプリケーション負荷試験実践入門
Webアプリケーション負荷試験実践入門Webアプリケーション負荷試験実践入門
Webアプリケーション負荷試験実践入門
 
偶然にも500万個のSSH公開鍵を手に入れた俺たちは
偶然にも500万個のSSH公開鍵を手に入れた俺たちは偶然にも500万個のSSH公開鍵を手に入れた俺たちは
偶然にも500万個のSSH公開鍵を手に入れた俺たちは
 
Scalaはじめました!
Scalaはじめました!Scalaはじめました!
Scalaはじめました!
 
プログラム組んだら負け!実はHTML/CSSだけでできること2015夏
プログラム組んだら負け!実はHTML/CSSだけでできること2015夏プログラム組んだら負け!実はHTML/CSSだけでできること2015夏
プログラム組んだら負け!実はHTML/CSSだけでできること2015夏
 
Scala超入門 - 2014/12/13 Scala関西勉強会
Scala超入門 - 2014/12/13 Scala関西勉強会Scala超入門 - 2014/12/13 Scala関西勉強会
Scala超入門 - 2014/12/13 Scala関西勉強会
 
中の下のエンジニアを脱出するための仕事術
中の下のエンジニアを脱出するための仕事術中の下のエンジニアを脱出するための仕事術
中の下のエンジニアを脱出するための仕事術
 
ウェブパフォーマンスの基礎とこれから
ウェブパフォーマンスの基礎とこれからウェブパフォーマンスの基礎とこれから
ウェブパフォーマンスの基礎とこれから
 
運用に自動化を求めるのは間違っているだろうか
運用に自動化を求めるのは間違っているだろうか運用に自動化を求めるのは間違っているだろうか
運用に自動化を求めるのは間違っているだろうか
 
プログラマのための線形代数再入門
プログラマのための線形代数再入門プログラマのための線形代数再入門
プログラマのための線形代数再入門
 
「スプラトゥーン」リアルタイム画像解析ツール 「IkaLog」の裏側
「スプラトゥーン」リアルタイム画像解析ツール 「IkaLog」の裏側「スプラトゥーン」リアルタイム画像解析ツール 「IkaLog」の裏側
「スプラトゥーン」リアルタイム画像解析ツール 「IkaLog」の裏側
 

Plus de hasegawa

Plus de hasegawa (15)

Midterm2nd
Midterm2ndMidterm2nd
Midterm2nd
 
Objc03 1
Objc03 1Objc03 1
Objc03 1
 
Objc05
Objc05Objc05
Objc05
 
Objc04
Objc04Objc04
Objc04
 
Objc03 2
Objc03 2Objc03 2
Objc03 2
 
Objc02
Objc02Objc02
Objc02
 
Cocoa Pro09
Cocoa Pro09Cocoa Pro09
Cocoa Pro09
 
Cocoa Pro08
Cocoa Pro08Cocoa Pro08
Cocoa Pro08
 
Cocoa Pro07
Cocoa Pro07Cocoa Pro07
Cocoa Pro07
 
Cocoa Pro6
Cocoa Pro6Cocoa Pro6
Cocoa Pro6
 
Cocoa Pro5
Cocoa Pro5Cocoa Pro5
Cocoa Pro5
 
Cocoa Pro4
Cocoa Pro4Cocoa Pro4
Cocoa Pro4
 
Cocoa Pro01
Cocoa Pro01Cocoa Pro01
Cocoa Pro01
 
Cocoa Pro03
Cocoa Pro03Cocoa Pro03
Cocoa Pro03
 
CocoaPro02
CocoaPro02CocoaPro02
CocoaPro02
 

Objc01