Objective-C

歴史

Objective-Cは、1983年にBrad CoxとTom Loveによって開発され、そのコンパイラやライブラリを支援するためにStepstone社を創立した。Stepstone社は、Objective-Cに力を注いだが、それはマイナーな存在であった。Objective-Cが認知され始めるきっかけは、1985年、アップルコンピュータを去ったスティーブ・ジョブズが、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年初頭、アップルが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が公開されて以降、習得者の人口が増える傾向にあるが、アップルの開発環境は、徐々にLLVMベースにシフトし、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
メソッドにはクラスメソッドとインスタンスメソッドがあり、それぞれ接頭辞+及び-により区別される。クラスメソッドはクラスオブジェクトの操作に、インスタンスメソッドはインスタンスオブジェクトの操作に使用される。クラスメソッドは特にインスタンスオブジェクトの生成にも使用される事が多い。インスタンスメソッドは、インスタンスオブジェクトにメッセージを送信した際に起動され、クラスメソッドはクラスオブジェクトにメッセージを送信した際に起動する。なお、インスタンスメソッドとクラスメソッドは全く同じ名前のセレクタを指定して定義できる。
いわゆるコンストラクタは存在しない。慣習として新規オブジェクトの生成は+allocで、初期化は-initで行われるが、プログラマが自由に別の特殊化したメソッドを定義することが可能であり、初期化中に別の初期化メソッドを呼びだす場合もある。一方デストラクタ（ファイナライザ）に相当するものは-dealloc、またはガベージコレクション使用時の-finalizeで、これらのメソッドはオブジェクトの破壊時に必ず呼び出される。
selfは特殊な変数で、メソッドの実行時に自動的にレシーバが代入される。再代入も可能であり、-init等でスーパークラスの実装で自分自身を初期化し、正しい値が返った時のみ継続して初期化を行なうなどに利用される。
オブジェクトの型はオブジェクトを特定のクラスに制限したい時に用いられる。ただしこれはソースコードでのみ意味を持ち、実行レベルでは全てidとして扱われる。また型付きのオブジェクトはインスタンス変数を構造体互換でアクセスできる。保護レベルはpublic（フリー）、protected（継承クラスのみ）、private（同一クラスのみ）があり、デフォルトはprotectedである。ただメモリ管理の一貫性などの理由から、ほとんどの場合アクセサを用いる。

互換性

32bit時代のLinux版GCCではNSObjectクラスではなく、Objectクラスが使用されていた。64bit時代になってからは、かろうじてObjectクラスの定義が残っているものの殆どのメソッドは削除され、かつてのようにNSObjectクラスの代わりに使用することは出来ない。ソースコードレベルでは完全に互換性が失われている。代わりにGNUStepを導入し、ヘッダーとして#import<Foundation/NSObject.h>を記述し、ObjectクラスをNSObjectクラスに変更する必要がある。また、ライブラリの指定も従来の-lobjcだけでは足りず、-lgnustep-baseの指定が必要となる。

リフレクション

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++

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、Rubyなどがある。

メモリ管理

初期のObjective-CプログラムはC同様単純な割当と解放を行なっていたが、現在は標準APIライブラリに実装された参照カウント方式のAutorelease poolを利用するのが標準的である。参照カウント方式ではあるがガベージコレクションとは異なり、半自動で行なわれる。方法としてはNSAutoreleasePoolクラスをインスタンス化し、ここに解放されるべきオブジェクトを、autoreleaseメッセージを用いて登録する。登録した全てのオブジェクトが不要になったらreleaseメッセージでNSAutoreleasePoolのインスタンスを解放する。すると登録されていたオブジェクトもすべて一斉に解放されるというものである。
ほかにもオブジェクト(仮にobjAとする)のイニシャライザ(初期化メソッド)を呼び出すと、その時点で自分をautoreleaseするオブジェクトを定義できる。そのようなオブジェクトをインスタンス化したユーザは、objAに対して明示的にautoreleaseせずとも、NSAutoreleasePoolのインスタンスをreleaseするだけでobjAを解放できることになる。例としてNSStringクラスがある。stringWithCString:で初期化するとautoreleaseされた状態になる。

手動管理の場合

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が解放されたので1count = [fooBar retainCount]; // Barをretainしていないのでエラーcount = [objBaz retainCount]; // ほかの参照がなかったので解放済み。エラーcount = [objBar retainCount]; // fooBarがretainしていなかったのでエラー
オーナー(所有者)とは、あるオブジェクトのインスタンスをretainまたはautoreleaseしたオブジェクトないしは変数のことで、上の例で、poolをreleaseする直前ではobjで始まる変数とquxが該当する。
OPENSTEPライブラリは、イベントサイクル単位でAutorelease poolと呼ばれる暗黙の参照元を持っており、オブジェクトをここに登録することでイベント終了時には自動で解放されるオブジェクトを実現している。Macに移植後もNSApplicationクラスに実装されているが、オブジェクトの登録も不要となっている。前述のNSAutoreleasePoolは、NSApplicationクラスが不要なときでも自動解放ができるように用意されたものといえる。
GNU版ランタイム及び、Mac向けのApple版ランタイム(Objective-C 2.0以降)ではガベージコレクションも利用可能だが、iOSに於てはリソースの効率上使用できない。Appleはさらに第三の方式としてARC (Automatic Reference Counting) 方式を開発したため、Apple系では今後この方式が主流になる見込みである。

自動参照カウント (ARC)

自動参照カウントは内部的にはretain/release/autoreleaseと同様のメカニズムで動作するが、コンパイル時にメソッドの命名ルール等を見て自動的にretain/release相当のコードを挿入する方式である。これにより、自動参照カウントでは明示的なretain/releaseがそもそも不可能になる。管理周りのコードの削減に加え、autorelease管理の実行効率が向上するため、旧方式のプログラムを自動参照カウントに切り替えるだけでもパフォーマンスがいくぶん向上する。