Objective-C
Objective-C(オブジェクティブ シー)は、プログラミング言語の一種。CをベースにSmalltalk型のオブジェクト指向機能を持たせた上位互換言語である。 Objective-CはNeXT、macOSのオペレーティングシステム (OS) に標準付属する公式開発言語である。macOSのパッケージ版に開発環境がDVDで付属するほか、ユーザ登録をすれば無償でダウンロードできる(Xcodeの項目参照)。現在では主にAppleのmacOSやiOS上で動作するアプリケーションの開発で利用される。 概要Objective-CはCを拡張してオブジェクト指向を可能にしたというよりは、Cで書かれたオブジェクト指向システムを制御しやすいようにマクロ的な拡張を施した言語である。したがって、「better C」に進んだC++とは異なり、「C & Object System」という考え方であり、ある意味2つの言語が混在した状態にある。 関数(メソッド)の定義と呼び出し方が独特であるため、Objective-Cのコードは一見C++以上にCとはかけ離れた独特の記述となる。しかし、言語仕様はCの完全上位互換であり、if/for/whileなどの制御文や、intなどのスカラー型、関数記法、宣言・代入といった基本的な文法はCに準拠する。一方オブジェクトシステムはSmalltalkの概念をほぼそのまま借用したもので、動的型のクラス型オブジェクト指向ランタイムを持ち、メッセージパッシングにより動作する。このことからしばしば「インラインでCの書けるSmalltalk」または「インラインでSmalltalkの書けるC」などと呼ばれる。Cとは異なるObjective-Cに特有の部分は、 最大の特徴はオブジェクトシステムが完全に動的という点で、実行時のクラス拡張、オブジェクト汎用型idの導入により型によらない動的配列・辞書など、インタプリタに近い記述力をもつことである。実際にコードそのものはネイティブコンパイルされるものの、動作原理はほぼインタプリタに近く、コンパイラ型言語としてはまれな柔軟性を発揮する。 したがって、C側から見れば一種のスクリプトインタプリタが乗っているような状態であり、逆にオブジェクトシステムからはOS機能や膨大なC言語資源を直接利用可能なインターフェースが備わっているといえる。また仮想マシンを持たずに済むため、取り回しも良い。パフォーマンスはJavaのような中間コード型言語よりも良好で、CやC++のようなネイティブコンパイル言語には劣るとされる。Objective-C特有のこの形態は双方のメリット・デメリットが明確で、実際的な使い勝手が非常に優れている。この特性に着目したのがNEXTSTEPで、UNIXとの互換性と先進的なオブジェクト指向環境の両立に成功し、その後のOS設計に大きな影響を与えることとなった。 後続言語への影響としては、特にJavaの基礎設計にその姿を見ることができる(サン・マイクロシステムズがOPENSTEP開発を共同で行なっていたことと関係がある[1])。
歴史Objective-Cは、1983年にBrad CoxとTom Loveによって開発され[2]、そのコンパイラやライブラリを支援するためにStepstone社を創立した。Stepstone社は、Objective-Cに力を注いだが、それはマイナーな存在であった。Objective-Cが認知され始めるきっかけは、1985年、Apple Computerを去ったスティーブ・ジョブズが、m68k機であるNeXTコンピュータとNeXTSTEPオペレーティングシステムの開発を行うNeXT Computer社を創立したことに始まる。 そのマシンのユーザインタフェースは、Display PostScriptとObjective-Cで書かれたApplication Kitにより提供され、Objective-CはNeXTコンピュータの主力言語となった。その後の歴史は、主にNeXT社とともにあり、GCCをベースにしたObjective-Cサポートが行われ、プロトコルの導入など文法の拡張なども行われている。NeXT社による多くの成果は、GCCに還元されている。 1995年には、NeXT社がStepstone社からObjective-C言語と、その商標に関する全ての権利を買い取っている。1997年初頭、AppleがNeXT社を買収し、2001年に登場したMac OS XのCocoaフレームワークのコア言語として採用されている。Mac OS X v10.5からは一部言語仕様の変更が行われObjective-C 2.0と呼ばれる(詳細は#Objective-C 2.0を参照)。 コンパイラおよび言語仕様は完全に公開されているものの、長らくNeXTおよびその後継であるmacOSとiOSの専用言語に近い状態にある。2008年にiPhone OS(現iOS)のAPIが公開されて以降、習得者の人口が増える傾向にあるが、Appleの開発環境は、徐々にLLVM/Clangにシフトし、GCC版は事実上のGNUstep専用と化している。 基本的な構文メッセージ送信C++とは異なり、オブジェクトのメソッド呼びだしにはSmalltalkのメッセージ式を模した新たな構文が導入されている。Objecitve-Cではこれをメッセージ式と呼び、メソッド呼びだしはメッセージ送信と呼ぶ。メッセージ送信は実行時のメッセージパッシングであり、その時渡されるメッセージ値をセレクタという。特徴的なのはSmalltalk同様キーワード引数形式をとることで、セレクタ名と引数値が交互に並んだ形態になる。なおSmalltalkにはあるカスケード式(一つのオブジェクトに続けてメッセージを送る)はない。 // メッセージの送信
// 単項メッセージ
[receiver msg];
// 引数付きメッセージ。この場合「msg:with:」でセレクタ一つ
val = [receiver msg: arg1 with: arg2];
// メッセージの入れ子
val = [obj1 msg: [obj2 msg]];
クラス定義Objective-Cのクラスは定義部と実装部に分かれており、通常定義部を.hファイル、実装部を.mファイルに記述する。後述のカテゴリによりクラス定義を複数のパートに分割できる。 // クラスの定義 (MyObject.h)
@interface MyObject : NSObject
{
int val;
id obj;
}
+ (void)classMethod:(id)arg; // クラスメソッド
- (id)method:(NSObject*)arg1 with:(int)arg2; // インスタンスメソッド。arg1は型付き
@end
// 実装 (MyObject.m)
@implementation MyObject
+ (void)classMethod:(id)arg
{
// some operation
}
- (id)method:(NSObject*)arg1 with:(int)args2
{
return obj;
}
// 典型的なinit
- (id)init
{
self = [super init]; // スーパークラスの呼びだし
if(self != nil)
{
val = 1;
obj = [[SomeClass alloc] init];
}
return self;
}
// deallocは自身のリソースを解放してからスーパークラスに回す
- (void)dealloc
{
[obj release];
[super dealloc];
}
@end
メソッドにはクラスメソッドとインスタンスメソッドがあり、それぞれ接頭辞 いわゆるコンストラクタは存在しない。慣習として新規オブジェクトの生成は selfは特殊な変数で、メソッドの実行時に自動的にレシーバが代入される。再代入も可能であり、 オブジェクトの型はオブジェクトを特定のクラスに制限したい時に用いられる。ただしこれはソースコードでのみ意味を持ち、実行レベルでは全て 互換性32bit時代のLinux版GCCではNSObjectクラスではなく、Objectクラスが使用されていた。64bit時代になってからは、かろうじてObjectクラスの定義が残っているものの殆どのメソッドは削除され、かつてのようにNSObjectクラスの代わりに使用することは出来ない。ソースコードレベルでは完全に互換性が失われている。代わりにGNUStepを導入し、ヘッダーとして 特徴リフレクションObjective-Cのオブジェクトは全て自分自身に関する定義情報を保持しており、実行時に利用することができる。 リフレクションの一部- (BOOL)respondsToSelector:(SEL)aSel;
- (BOOL)isKindOfClass:(Class)cls;
転送実装系によるが、存在しないメソッドを呼びだした際、例外を発生する前にそれを他のオブジェクトに転送するチャンスが与えられる。Message Forwardingと呼ばれる。 転送の例Appleのランタイムでは、セレクタに対応する引数情報と転送処理の二つの過程を経て行われる。 // NSMethodSignatureはメソッドの引数の数や型情報を表すオブジェクト
// Objective-Cのメソッドはスカラー型をとるC関数互換なので正確なスタック情報が必要となる
- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel
{
id signature = [otherObj methodSignatureForSelector: sel];
return signature;
}
// NSInvocationはターゲットと引数を持ち、返値を受け取るオブジェクト
// 元のターゲットの代わりに別のターゲットで実行を行なうとあたかもそのオブジェクトにメッセージが送られたかのように動作する
- (void)forwardInvocation:(NSInvocation*)invocation
{
SEL aSel = [invocation selector];
if([otherObj respondsToSelector: aSel])
{
[invocation invokeWithTarget: otherObj];
}
else
{
[self doesNotRecognizeSelector: aSel];
}
}
プロトコルプロトコルはクラスのメソッドインターフェースを規定する機構である。元々はNeXTワークステーション上で分散オブジェクトシステムを構成する際、リモートオブジェクトの通信効率を上げるために導入された。 プロトコルに準拠するクラスは定義されたメソッドを全て実装しなければならない。また、プロトコルは多重継承を許す。 プロトコルの例// プロトコルを定義する
@protocol MyProtocol <NSObject, NSCopying>
- (void)methodForRespond;
@end
// プロトコルを導入する
@interface MyObject <MyProtocol>
...
@end
類似した機構に実装をオプションにできるinformalプロトコルがある。これは後述のカテゴリのうち、インターフェース定義のみを利用する方法で、利用側は実装状態をリフレクションで調査して正当な場合のみ呼びだす。 // ここでは「NSObjectにはtransferAcceptable:が定義されている」と宣言している
// 実際には対応する実装を用意する義務がないため、NSObjectを継承したオブジェクトがそれを行なった場合のみ利用できる
@interface NSObject (OptionalMethods)
- (BOOL)transferAcceptable:(id)obj;
@end
- (void)method
{
id val,obj;
...
if([obj respondsToSelector: @selector(transferAcceptable:)])
{
[obj transferAcceptable: val];
...
}
}
カテゴリカテゴリは、クラス定義をグループに分割したり、既存のクラスにメソッドを追加したりするための言語機能。Smalltalkが統合開発環境上でクラスとメソッドの表示を整理するために使っているクラスカテゴリーとメソッドカテゴリー(プロトコルとも言う)をそのまま取り込んだ。 クラスの実装を関連するメソッド群毎に別々の場所に分割して記述することを可能とする目的で作られた。 このほか、カテゴリに宣言したメソッドが実行時にカテゴリがロードされたタイミングでクラスへ追加される、という性質を応用して、ソースコードを直接修正できないクラスに対してサブクラスを定義せずにメソッドを追加する、といった用途や、Informalプロトコルの定義等にも用いられる。 カテゴリメソッドで既存のメソッドをオーバーライドすることも可能であるが、推奨されていない。 カテゴリの例@interface NSObject (BetterHash)
- (unsigned)hash;
@end
@implementation NSObject (BetterHash)
// 子孫クラスのうち、独自のオーバーライドのない-hashは全てこの実装に置き換わる
- (unsigned)hash
{
return better_hash_function(self);
}
@end
メモリ管理初期のObjective-CプログラムはC同様単純な割当と解放を行なっていたが、現在は標準APIライブラリに実装された参照カウント方式のAutorelease poolを利用するのが標準的である。参照カウント方式ではあるがガベージコレクションとは異なり、半自動で行なわれる。 方法としてはNSAutoreleasePoolクラスをインスタンス化し、ここに解放されるべきオブジェクトを、 ほかにもオブジェクト(仮に 手動管理の場合int count = 0;
// オブジェクトのインスタンス化
// これら2つは自動的に参照カウントが1になる
id objFoo = [[Foo alloc] init];
id objBar = [[Bar alloc] init];
id baz = objFoo; // Fooを間接参照
id qux = objBar; // Barを間接参照
[baz retain]; // Fooの参照カウントは2になる
// オブジェクトの解放
[objFoo release]; // objFooは解放されるがFooの参照カウントは1で維持される
[objBar release]; // 変数quxはretainしていないのでBarは破棄される
[baz release]; // Fooが解放される
count = [qux retainCount]; // Barの参照カウントを参照する。Barは解放されているのでエラー
NSAutoreleasePoolの例int count = 0;
// NSAutoreleasePoolのインスタンス化
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id objFoo = [[Foo alloc] init];
id objBar = [[Bar alloc] init];
id objBaz = [[Baz alloc] init];
// Autorelease poolに登録
[objFoo autorelease];
[objBar autorelease];
[objBaz autorelease];
// オブジェクトを参照する変数
id qux = objFoo;
id fooBar = objBar;
[qux retain]; // FooのretainCountは2
// Autorelease poolの解放
[pool release];
count = [qux retainCount]; // Fooをretain後オーナーだったobjFooが解放されたので1
count = [fooBar retainCount]; // Barをretainしていないのでエラー
count = [objBaz retainCount]; // ほかの参照がなかったので解放済み。エラー
count = [objBar retainCount]; // fooBarがretainしていなかったのでエラー
オーナー(所有者)とは、あるオブジェクトのインスタンスを OPENSTEPライブラリは、イベントサイクル単位でAutorelease poolと呼ばれる暗黙の参照元を持っており、オブジェクトをここに登録することでイベント終了時には自動で解放されるオブジェクトを実現している。Macに移植後もNSApplicationクラスに実装されているが、オブジェクトの登録も不要となっている。前述のNSAutoreleasePoolは、NSApplicationクラスが不要なときでも自動解放ができるように用意されたものといえる。 GNU版ランタイム及び、Mac向けのApple版ランタイム(Objective-C 2.0以降)ではガベージコレクションも利用可能だが、iOSに於てはリソースの効率上使用できない。Appleはさらに第三の方式としてARC (Automatic Reference Counting) 方式を開発した。またガベージコレクションはOS X 10.11を最後に廃止[4]されており、それ以降Apple系ではARCが主流となっている。 自動参照カウント (ARC)自動参照カウントは内部的には その他メタ言語的メカニズムオブジェクトシステムは動的ディスパッチを行い、オブジェクトシステム自体がCで書かれていることに加え、C哲学である「プログラマにできることを制限しない」を良くも悪くも受け継いでいるため、Objective-Cにはさまざまな超言語的技法が存在している。これらの機能は非常に強力であるため乱用を避けるべきだが、この柔軟性こそがObjective-Cの魅力と評する向きもある。
Objective-C++Objective-CとC++が混在したもの。両者はCからの拡張部分がほぼ干渉しないため、お互いをただのポインタ値と見なすことで表記が混在できる。したがってクラスシステムの互換性はなく、単純なObjective-C & C++になる。拡張子は.mm。 関数やObjective-Cメソッドの内部では、Objective-CとC++両方の機能を任意に組み合わせて利用することができる。例えば、Objective-Cオブジェクトの寿命を管理するスマートポインタを、C++の機能を用いて作成するようなことが可能である。他方、クラスの階層はObjective-CとC++で完全に分かれており、一方が他方を継承することは全く不可能である。また、伝統的なObjective-Cの例外処理とC++のそれは互換性がなく、プログラマが両者を逐一捕捉・変換しなければ、メモリリークやクラッシュにつながる。 主な用途はC++のライブラリをObjective-Cからアクセスするためのラッパー記述で、実例としてAppleのWebKit(KHTMLベース)などがある。コンパイル速度が非常に遅くなることもあって積極的に用いられることは少ない。 言語ブリッジ上述のように、Objective-Cランタイムシステムの実体はC言語関数群そのものである。このライブラリの内部でリフレクションやメッセージ送信の機構が全て閉じているため、これらに対するラッパーを用意することで、外部言語からシステムの完全制御が可能になる。 現在言語ブリッジが確立している言語には、Smalltalk、Haskell、Java、Perl[要出典]、Python(PyObjC)、Ruby(RubyCocoa)などがある。 処理系の特性
Objective-C 2.0AppleはMac OS X v10.5においてObjective-C 2.0という名称で言語仕様の変更を行った。
脚注
参考文献Objective-Cに関する最初の本であり、Objective-Cを利用したオブジェクト指向システム開発に関する本。
この本はObjective-CのNeXTSTEPでの実装について記述している。NeXTSTEPが明確なターゲットだが、Objective-Cを学習するためのよい入門書となる。
多くの実例とともに、Stepstone版とNeXT版のObjective-Cについて、両者の違いを含めて論じている。
Objective-C、C++、Smalltalk、Object Pascal、Javaを比較しながら、オブジェクト指向プログラミングの話題を紹介している。
外部リンク
|
Portal di Ensiklopedia Dunia