クラッシーココア
科学者のためのCocoa(パート2):クラッシーココア
英語のチュートリアルの作成日:2006年10月31日
著者:Drew McCormack
翻訳:Ignacio Enriquez
この科学者向けのCocoaのチュートリアルの第一回では、Objective-Cのコードとのファーストコンタクトをとることができましたね。うまくいけば、現在、その遭遇のショックから十分回復していることと思います。ここからはもう少し深く掘り下げていきたいと思います。
このパートではObjective-Cのオブジェクト指向の構成要素を注意深く見ていきます:クラスとオブジェクトです。
オブジェクト指向プログラミングとむだ骨
多くの科学者/研究者がオブジェクト指向(OO)スタイルのプログラミングに自然に反感を持ちます。結果的に”遅い”コードになるとか、他者は見ることができない小さいブラックボックスにアルゴリズムの微妙さを隠すなどと主張する者がいますが、事実上、このような議論はある程度根づいているが、大抵誇張であるか、またはポイントから完全に外れています。
実際にオブジェクト指向プログラミング(OOP)は理由があって科学界の外で著しく成功しているのですから、もうむだ骨を折ることは止めて この新しいパラダイムを取り入れるときでしょう。チェッ、Fortranさえ現在、OOPをサポートしてるんだぜぇ!
私は皆さんのお手伝いをする為にここにいます、そしてできれば好きになっていただきたい。
クラス対型
初対面のときOOPは恐らく完全に未知の生き物に見えるかもしれませんが、しばらくすると 足が4本あって、頭があって、そして尻尾もあることに気付きます。実際に手続き型プログラミングモデルの進化過程から自然に生まれたもので、多くの科学者、研究者は既に慣れています。
例えば、クラスを挙げましょう。クラスはOOプログラムのビルの基本コンクリートブロックであります。Cでは構造体は変数を持っているデータの型を定義します。例えば、分子シミュレーションのパッケージに以下のものが見つかるかもしれません。
Atomはmassとpositionの変数を持っている型です。これらの変数が一緒にAtomを構成します。
Objective-Cでは、Atomはクラスになれる。
ご覧いただければお分かりのように、これらは異なっていません。現在構文は@interfaceと@endのトークンを含んでいます、そしてまた違うクラス(NSObject)が出現しました。でも、それ以外は殆ど同じです。
それで、前記のステロイドはどこか?、さて1番目はかなり潔白なNSObjectリファレンスに隠れています。これはAtomのスパークラスであります。AtomはNSObjectで定義された全てのものを再利用できます - AtomはNSObjectの全てを継承します。シリーズの継承とポリモーフィズム(多態性)を説明するときにこれについて詳細に議論します。
2番目の増進は実はこの例ではそれほど明確ではありませんが、終わりの中括弧と@endキーワードの間にあるスペースと関係があります。このスペースではメソッドを定義することができます。それらは基本的にこのクラスに属する関数であります。クラスの変数は状態を定義するならば、メソッドは振る舞いを定義するのです。
従って、クラスは本当にハイレベルの型です - 状態と振舞いがあるタイプ。 ただそれではなく、一つのクラスは別のクラスを継承することができ、沢山の変数とメソッドを得します。
うまくいけば、このコースが進むにつれて クラスのこの外観上無毒な属性がどれくらい役に立つかを理解し始めるでしょう。
Class Interface
このクラスの議論をより客観的にするために、数字を足し算を行う簡単なものを書きます。クラスの名前はAdditionOperatorになります。そして、唯一の目的は二つの10進数の足し算を行うことです。
始めに クラスのインターフェースブロックを定義します。インターフェースはクラスの変数とメソッドを定義します。
Cベースの言語のように、クラスのインターフェースはヘッダーファイルで定義されます。この場合、上記のコードは
'AdditionOperator.h'になります。
このコード自体は(前回初めて会った)Foundationフレームワークをインポートするところから始まっています。Foundationはユーザーインターフェースに直接関係していない多くのクラスを含みます。例えば、ストリングのクラス、アレイのようなコンテナクラスと辞書のクラスなどなどです。NSobjectのクラスもFoundationに含まれています。だからここで必要となっています。実践では、常にFoundationフレームワークをインクルードするかCocoaフレームワークを全部入れるかどちらかですね。
フレームワークを使用するために #importというプリプロセッサ指示を使います。#importはObjective-CのCの標準指示の拡張の一つであります。Cでは#include指示を使ってファイルをインポートをします。しかし、あるファイルを2回以上インポートするときに発生する競合を防ぐことはできません。#importはそれを防ぎます。(既にインポートしているかどうかのチェックを行い、インポートしていたらスキップします。)
#import指示でフレームワークをインポートするとき、山括弧を使用しインクルードファイルをフレームワーク名の後に書くことに注意しましょう。これはMac OS Xは2レベルのネームスペースを使用するからです。名前の競合を避けることがアイディアです:例えばFoundation.hファイルをインクルードされた別のフレームワークがあったら FoundationフレームワークのFoundation.hファイルのと競合し、プロセッサは混乱しどちらをインポートするか分からなくなります。これを解決するにはフレームワーク名をインポート指示に書きます。
それではクラスのインターフェース自体に入ります。AdditionOperatorはNSObjectのサブクラス;よってNSObjectの全ての変数とメソッドを継承します。後で説明しますが、Cocoaの全てのクラスは最終的にNSObjectを継承します。NSObjectに幾つかのメソッドがあります。オブジェクトの初期化、削除を含み、メモリの管理関連のメソッドもあります。やがてこれらに出くわします。
クラスの変数はインスタンス変数(instance variablesあるいは、短くivars)として知られています。これはクラスのオブジェクト(あるいはインスタンス)に属するからです。AdditionOperatorの場合 2つあります:leftValueとrightValueです。これらは'5 + 2'のような足し算の左値と右値を意味するように設計されています。この例ではleftValueは5でrightValueは2になります。
AdditionOperatorは新しいメソッド2つを定義しています:initWithLeftValue:andRightValue:とevaluateです。
(「新しい」と言ったのはNSObjectで定義されたメソッドも含むからです。)前者は初期化メソッドで 新しく作られたオブジェクトを有効な状態にします。Objective-Cでは新しくオブジェクトを作るとき いつも初期化メソッドが呼ばれます。後者は計算する為に呼び出されています。つまり、左値を右値と足し算するのです。
メソッドの定義の構文に少し困惑したかもしれませんが、はっきりさせるために初期化メソッドを見てみましょう
The syntax of the method declarations may have you a bit perplexed. Let's consider the initializer in order to clarify things:
ハイフンで始まります。これはインスタンスメソッドであることを示しています。そしてクラス自体に属するよりもクラスのオブジェクト(あるいはインスタンス)に属しています。今は意味が分からないかもしれませんが、後で明確になります。
クラスに属するメソッドを書きたいなら、例えばクラス内の全てのオブジェクトが共有するメソッド、ハイフンの代わりにプラス記号(+)を使用します。
メソッドの命名として (チュートリアルの1回目で導入された)セグメンテーションされたスタイルを使用しています。違いはコロンの次に丸括弧で囲まれた引数の型があります。これは引数lvとrvがdouble型(倍精度実数型)でないといけないのです。
ハイフンの次に丸括弧で囲まれたid型もあることに気付いていると思いますが、これはメソッドが返す値の型です。返す値がなければvoidをここに入れるべきです。Cと同様です。Objective-Cのid型は特別であって任意のクラスのオブジェクトのことを意味します。
クラスの実装
ヘッダーファイルはクラスのパブリックインターフェースを定義します。それの変数とメソッドです。メソッドの実装はまた別の.m拡張子を持ったファイルにあります。AdditionOperatorの場合、以下のような実装はAdditionOperator.mにあります。
このファイルもインポートから始まります。ヘッダーファイルのインポートです。コンパイラーはクラスのインターフェースのことを知るために必要です。自分のヘッダーファイルをインポートする時にフレームワーク用の山括弧でなくダブルコーテーションマーク(")を使用することに注意しましょう。
インターフェースブロックと同様に、クラスの実装は@implementationと@endトークンの間にあります。ヘッダーファイルで宣言されたメソッド(そして多くの宣言されなかったメソッドも)は実装のブロックで定義します。(実装のブロックにあってインターフェースブロックにないメソッドの例は、スパークラスで宣言されたメソッドです。これはオバーライドといい、今後のチュートリアルでも説明します。)
メソッドの実装では、勿論渡された引数を参照することができますが、クラスのインスタンス変数も参照できます。例えば、初期化メソッドでは、インスタンス変数leftValueとrightValueに引く数のlvとrvの値がそれぞれ割り当てられています。
インスタンス変数は初期化されているオブジェクトに属する;そのオブジェクトがあらゆるインスタンスメソッドから特別な変数であるselfを用いて参照できます。(C++やJavaに慣れている方にはthisポインターと同じです。)インスタンス変数を設定することにおいて 以下のコードは丁度同じぐらい有効でしょう。
leftValueとrightValueは実際にselfオブジェクトに含まれています、他の変数と曖昧さ(名前の競合)がないかぎり明示的にこう書く必要はありません。
魔法にかかったようにあらゆるインスタンスメソッドに渡されるselfの特別な変数以外に、superもあります。super変数は少しselfに似ています ー superはインスタンスメソッドを属するオブジェクトへのポインターです ー しかし大きな違いが一つあります:superはスーパークラスのメソッドや変数にアクセスするのに使われます。
上記の初期化メソッドでは、superによってinitが呼び出されます。これは NSobjectのinitメソッドが呼び出されることを意味しています。たとえAdditionOperatorクラスのinitメソッドがあっても(〜今はありませんが〜)これは呼び出されません。それはsuperを使うとこのクラスではなく、スーパークラスを探すことを言っているからです。
試してみましょう
この簡単なクラスを試してみるには ファイル(AdditionOperator.hとAdditionOperator.m)を作成します。そして以下の内容を持ったmain.mファイルを作ります。
To test this simple class, generate the AdditionOperator files (ie 'AdditionOperator.h' and 'AdditionOperator.m'), and then create a file called 'main.m' with the following contents:
これによって、左値として4.0と右値として5.0を持ったAdditionOperatorの一つのオブジェクトを作ります。そして計算された結果を出力します。main関数内のわからないことをあまり心配しないでください、なぜならそれは今度のチュートリアルで説明されるからです。
コンパイルと実行するには以下のコマンドを打ちます。
出力は以下のようになるはずです。
The output should look like similar to this:
Stay Tuned
今日は クラスの内部に初めて触れましたね。次回はもっと細かくクラスはどのように働くのかを見ます。オブジェクトの初期化から継承までやります。その間に今日導入された概念に慣れるようにしてください。オブジェクト指向は多くの科学者・研究者にとって新しい概念かもしれませんが、こつこつと勉強していけば、全てが分かるようになるまで長くかからないでしょう。最初は専門用語ばかりと抽象的な概念に見えますが、実はこの狂気には理由があります。そしてだんだんわかるようになり、「スーパークラスの指定された初期化メソッドを呼び出します」を易々と読めるようになります。
英語のチュートリアルの作成日:2006年10月31日
著者:Drew McCormack
翻訳:Ignacio Enriquez
この科学者向けのCocoaのチュートリアルの第一回では、Objective-Cのコードとのファーストコンタクトをとることができましたね。うまくいけば、現在、その遭遇のショックから十分回復していることと思います。ここからはもう少し深く掘り下げていきたいと思います。
このパートではObjective-Cのオブジェクト指向の構成要素を注意深く見ていきます:クラスとオブジェクトです。
オブジェクト指向プログラミングとむだ骨
多くの科学者/研究者がオブジェクト指向(OO)スタイルのプログラミングに自然に反感を持ちます。結果的に”遅い”コードになるとか、他者は見ることができない小さいブラックボックスにアルゴリズムの微妙さを隠すなどと主張する者がいますが、事実上、このような議論はある程度根づいているが、大抵誇張であるか、またはポイントから完全に外れています。
実際にオブジェクト指向プログラミング(OOP)は理由があって科学界の外で著しく成功しているのですから、もうむだ骨を折ることは止めて この新しいパラダイムを取り入れるときでしょう。チェッ、Fortranさえ現在、OOPをサポートしてるんだぜぇ!
私は皆さんのお手伝いをする為にここにいます、そしてできれば好きになっていただきたい。
クラス対型
初対面のときOOPは恐らく完全に未知の生き物に見えるかもしれませんが、しばらくすると 足が4本あって、頭があって、そして尻尾もあることに気付きます。実際に手続き型プログラミングモデルの進化過程から自然に生まれたもので、多くの科学者、研究者は既に慣れています。
例えば、クラスを挙げましょう。クラスはOOプログラムのビルの基本コンクリートブロックであります。Cでは構造体は変数を持っているデータの型を定義します。例えば、分子シミュレーションのパッケージに以下のものが見つかるかもしれません。
struct Atom {
double mass;
double position[3];
}
Atomはmassとpositionの変数を持っている型です。これらの変数が一緒にAtomを構成します。
Objective-Cでは、Atomはクラスになれる。
@interface Atom : NSObject {
double mass;
double position[3];
}
@end
ご覧いただければお分かりのように、これらは異なっていません。現在構文は@interfaceと@endのトークンを含んでいます、そしてまた違うクラス(NSObject)が出現しました。でも、それ以外は殆ど同じです。
それで、前記のステロイドはどこか?、さて1番目はかなり潔白なNSObjectリファレンスに隠れています。これはAtomのスパークラスであります。AtomはNSObjectで定義された全てのものを再利用できます - AtomはNSObjectの全てを継承します。シリーズの継承とポリモーフィズム(多態性)を説明するときにこれについて詳細に議論します。
2番目の増進は実はこの例ではそれほど明確ではありませんが、終わりの中括弧と@endキーワードの間にあるスペースと関係があります。このスペースではメソッドを定義することができます。それらは基本的にこのクラスに属する関数であります。クラスの変数は状態を定義するならば、メソッドは振る舞いを定義するのです。
従って、クラスは本当にハイレベルの型です - 状態と振舞いがあるタイプ。 ただそれではなく、一つのクラスは別のクラスを継承することができ、沢山の変数とメソッドを得します。
うまくいけば、このコースが進むにつれて クラスのこの外観上無毒な属性がどれくらい役に立つかを理解し始めるでしょう。
Class Interface
このクラスの議論をより客観的にするために、数字を足し算を行う簡単なものを書きます。クラスの名前はAdditionOperatorになります。そして、唯一の目的は二つの10進数の足し算を行うことです。
始めに クラスのインターフェースブロックを定義します。インターフェースはクラスの変数とメソッドを定義します。
#import <Foundation/Foundation.h>
@interface AdditionOperator : NSObject {
double leftValue, rightValue;
}
-(id)initWithLeftValue:(double)lv andRightValue:(double)rv;
-(double)evaluate;
@end
Cベースの言語のように、クラスのインターフェースはヘッダーファイルで定義されます。この場合、上記のコードは
'AdditionOperator.h'になります。
このコード自体は(前回初めて会った)Foundationフレームワークをインポートするところから始まっています。Foundationはユーザーインターフェースに直接関係していない多くのクラスを含みます。例えば、ストリングのクラス、アレイのようなコンテナクラスと辞書のクラスなどなどです。NSobjectのクラスもFoundationに含まれています。だからここで必要となっています。実践では、常にFoundationフレームワークをインクルードするかCocoaフレームワークを全部入れるかどちらかですね。
フレームワークを使用するために #importというプリプロセッサ指示を使います。#importはObjective-CのCの標準指示の拡張の一つであります。Cでは#include指示を使ってファイルをインポートをします。しかし、あるファイルを2回以上インポートするときに発生する競合を防ぐことはできません。#importはそれを防ぎます。(既にインポートしているかどうかのチェックを行い、インポートしていたらスキップします。)
#import指示でフレームワークをインポートするとき、山括弧を使用しインクルードファイルをフレームワーク名の後に書くことに注意しましょう。これはMac OS Xは2レベルのネームスペースを使用するからです。名前の競合を避けることがアイディアです:例えばFoundation.hファイルをインクルードされた別のフレームワークがあったら FoundationフレームワークのFoundation.hファイルのと競合し、プロセッサは混乱しどちらをインポートするか分からなくなります。これを解決するにはフレームワーク名をインポート指示に書きます。
それではクラスのインターフェース自体に入ります。AdditionOperatorはNSObjectのサブクラス;よってNSObjectの全ての変数とメソッドを継承します。後で説明しますが、Cocoaの全てのクラスは最終的にNSObjectを継承します。NSObjectに幾つかのメソッドがあります。オブジェクトの初期化、削除を含み、メモリの管理関連のメソッドもあります。やがてこれらに出くわします。
クラスの変数はインスタンス変数(instance variablesあるいは、短くivars)として知られています。これはクラスのオブジェクト(あるいはインスタンス)に属するからです。AdditionOperatorの場合 2つあります:leftValueとrightValueです。これらは'5 + 2'のような足し算の左値と右値を意味するように設計されています。この例ではleftValueは5でrightValueは2になります。
AdditionOperatorは新しいメソッド2つを定義しています:initWithLeftValue:andRightValue:とevaluateです。
(「新しい」と言ったのはNSObjectで定義されたメソッドも含むからです。)前者は初期化メソッドで 新しく作られたオブジェクトを有効な状態にします。Objective-Cでは新しくオブジェクトを作るとき いつも初期化メソッドが呼ばれます。後者は計算する為に呼び出されています。つまり、左値を右値と足し算するのです。
メソッドの定義の構文に少し困惑したかもしれませんが、はっきりさせるために初期化メソッドを見てみましょう
The syntax of the method declarations may have you a bit perplexed. Let's consider the initializer in order to clarify things:
-(id)initWithLeftValue:(double)lv andRightValue:(double)rv;
ハイフンで始まります。これはインスタンスメソッドであることを示しています。そしてクラス自体に属するよりもクラスのオブジェクト(あるいはインスタンス)に属しています。今は意味が分からないかもしれませんが、後で明確になります。
クラスに属するメソッドを書きたいなら、例えばクラス内の全てのオブジェクトが共有するメソッド、ハイフンの代わりにプラス記号(+)を使用します。
メソッドの命名として (チュートリアルの1回目で導入された)セグメンテーションされたスタイルを使用しています。違いはコロンの次に丸括弧で囲まれた引数の型があります。これは引数lvとrvがdouble型(倍精度実数型)でないといけないのです。
ハイフンの次に丸括弧で囲まれたid型もあることに気付いていると思いますが、これはメソッドが返す値の型です。返す値がなければvoidをここに入れるべきです。Cと同様です。Objective-Cのid型は特別であって任意のクラスのオブジェクトのことを意味します。
クラスの実装
ヘッダーファイルはクラスのパブリックインターフェースを定義します。それの変数とメソッドです。メソッドの実装はまた別の.m拡張子を持ったファイルにあります。AdditionOperatorの場合、以下のような実装はAdditionOperator.mにあります。
#import "AdditionOperator.h"
@implementation AdditionOperator
-(id)initWithLeftValue:(double)lv andRightValue:(double)rv {
[super init];
leftValue = lv;
rightValue = rv;
return self;
}
-(double)evaluate {
return leftValue + rightValue;
}
@end
このファイルもインポートから始まります。ヘッダーファイルのインポートです。コンパイラーはクラスのインターフェースのことを知るために必要です。自分のヘッダーファイルをインポートする時にフレームワーク用の山括弧でなくダブルコーテーションマーク(")を使用することに注意しましょう。
インターフェースブロックと同様に、クラスの実装は@implementationと@endトークンの間にあります。ヘッダーファイルで宣言されたメソッド(そして多くの宣言されなかったメソッドも)は実装のブロックで定義します。(実装のブロックにあってインターフェースブロックにないメソッドの例は、スパークラスで宣言されたメソッドです。これはオバーライドといい、今後のチュートリアルでも説明します。)
メソッドの実装では、勿論渡された引数を参照することができますが、クラスのインスタンス変数も参照できます。例えば、初期化メソッドでは、インスタンス変数leftValueとrightValueに引く数のlvとrvの値がそれぞれ割り当てられています。
インスタンス変数は初期化されているオブジェクトに属する;そのオブジェクトがあらゆるインスタンスメソッドから特別な変数であるselfを用いて参照できます。(C++やJavaに慣れている方にはthisポインターと同じです。)インスタンス変数を設定することにおいて 以下のコードは丁度同じぐらい有効でしょう。
self->leftValue = lv;
self->rightValue = rv;
leftValueとrightValueは実際にselfオブジェクトに含まれています、他の変数と曖昧さ(名前の競合)がないかぎり明示的にこう書く必要はありません。
魔法にかかったようにあらゆるインスタンスメソッドに渡されるselfの特別な変数以外に、superもあります。super変数は少しselfに似ています ー superはインスタンスメソッドを属するオブジェクトへのポインターです ー しかし大きな違いが一つあります:superはスーパークラスのメソッドや変数にアクセスするのに使われます。
上記の初期化メソッドでは、superによってinitが呼び出されます。これは NSobjectのinitメソッドが呼び出されることを意味しています。たとえAdditionOperatorクラスのinitメソッドがあっても(〜今はありませんが〜)これは呼び出されません。それはsuperを使うとこのクラスではなく、スーパークラスを探すことを言っているからです。
試してみましょう
この簡単なクラスを試してみるには ファイル(AdditionOperator.hとAdditionOperator.m)を作成します。そして以下の内容を持ったmain.mファイルを作ります。
To test this simple class, generate the AdditionOperator files (ie 'AdditionOperator.h' and 'AdditionOperator.m'), and then create a file called 'main.m' with the following contents:
#import "AdditionOperator.h"
int main() {
AdditionOperator *operator = [[AdditionOperator alloc] initWithLeftValue:4.0
andRightValue:5.0];
NSLog(@"4.0 + 5.0 is %f", [operator evaluate]);
[operator release];
return 0;
}
これによって、左値として4.0と右値として5.0を持ったAdditionOperatorの一つのオブジェクトを作ります。そして計算された結果を出力します。main関数内のわからないことをあまり心配しないでください、なぜならそれは今度のチュートリアルで説明されるからです。
コンパイルと実行するには以下のコマンドを打ちます。
gcc -ObjC AdditionOperator.m main.m -framework Foundation
./a.out
出力は以下のようになるはずです。
The output should look like similar to this:
2006-10-31 14:24:07.758 a.out[1470] 4.0 + 5.0 is 9.000000
Stay Tuned
今日は クラスの内部に初めて触れましたね。次回はもっと細かくクラスはどのように働くのかを見ます。オブジェクトの初期化から継承までやります。その間に今日導入された概念に慣れるようにしてください。オブジェクト指向は多くの科学者・研究者にとって新しい概念かもしれませんが、こつこつと勉強していけば、全てが分かるようになるまで長くかからないでしょう。最初は専門用語ばかりと抽象的な概念に見えますが、実はこの狂気には理由があります。そしてだんだんわかるようになり、「スーパークラスの指定された初期化メソッドを呼び出します」を易々と読めるようになります。
コメント
コメントを投稿