The comp.lang.objective-C FAQ listing 日本語訳

目次

メタFAQ

あなたは誰ですか

このFAQの著者はGraham Leeです。 この文書に対するコメントと訂正は彼に送ってください。この文書を執筆するに あたっては、comp.lang.objective-cの 人達の計り知れないほど貴重な手助けがありました。とくにMichael Ashには、 カテゴリ、プロトコル、スレッド同期のセクションの執筆と、セレクタと 実装ポインタのセクションについて多くのアドバイスをもらいました。

comp.lang.objective-c FAQはすでにあるのではないですか

そのとおり、すでにあります。Devid Stesがメインテナンスしている公式FAQが rtfm.mit.edu にあります。しかし、そのFAQの内容は古く、GNUとAppleのObjective-Cに関する セクションに多くの誤りや取り上げていない情報があります。そこで私は、Davidがまだ 更新していないFAQに最新の情報を提供することにしました。このFAQは、オリジナル のFAQに代わって、広範囲にわたる正確な最新情報を提供することを目指しています。 できれば、このFAQがcomp.lang.objective-cに おける、より完全で正確なFAQになればよいと思っています。 最近、私はこのFAQからPOCに特有な情報を除くことにしました。というのは、 このUsenetグループには、POCに関心がある人はほとんどいないように思えたからです。 POCに関心がある人は、今でも、前述の公式FAQから情報を得ることができます。 このFAQでは、gcc版のObjective-Cのサポートに集中し、もっと役に立つ、最新のFAQの メインテナンスをしたいと思っています。

このFAQの最新版はどこにありますか

今、あなたが読んでいるFAQは、2007年2月14日に更新されたものです。 最新版は、thaesofereode.infoで みつけることができるでしょう。また、このFAQの公開場所のお知らせが、毎月 comp.lang.objective-cに投稿されます。

日本語訳について

この文書は原著者の許可を得て翻訳、公開しています。

著者: Graham Lee
原文: The comp.lang.objective-C FAQ listing
日本語訳: 神宮信太郎(jin@libjingu.jp)

日本語訳に関するコメント、誤りの指摘などありましたら、訳者までお知らせください。

日本語訳にあたり、以下の文献を参考にさせていただきました。

Objective-C言語

Objective-Cとは何ですか

Objective-Cは1980年代の始めごろ、Brad Cox博士が考案しました。 Objective-Cは、人気のあるC言語の上に作られた、オブジェクト指向を 実現する層として開発されました(このFAQとその他のObjective-Cに関する多くの 文書では、C言語またはそれに近い言語の知識があることを前提とします)。 Cox博士が実装したオリジナルのObjective-Cは、Cコードを生成するプリプロセッサ として実現され、Stepstoneの製品として販売されました。
Objective-Cは、C言語の純粋なスーパーセットです。そのため、コンパイラが サポートするC言語のバージョンによって、Objective-Cは変化することがあります。 C言語に追加された機能はわずかで、ほんの少しの新しいキーワード、3つの型(そのうち のidだけが最もよく使われる)、そしてメッセージ送信の構文が 追加されました。

Objective-Cの標準規格はどこにありますか

Objective-Cには、C、C++、その他の言語にあるような標準規格の文書はありません。 したがって、Objective-Cの公式な定義はその実装によって示されます。

Objective-Cにはどのような種類がありますか。それらはどれくらい使われていますか

最も普及していて、活発に開発されているObjective-Cは、Appleがメインテナンス している、GNU Cコンパイラをベースにしたものです。 Free Software Foundationによる通常のGNU CコンパイラもObjective-Cをサポート しており、その実装はAppleのものととてもよく似ていますが、新機能の追加に 少々時間がかかります。また、両者のランタイムには互換性がありません。 David StesによるPOCというObjective-Cの実装もありますが、前述のふたつとは 互換性がなく、AppleのObjective-Cが提供している機能の多くを含みません。 しかしその一方で、POCはブロック(Smalltalkの機能。Rubyにもみられる)と クラス変数をサポートしています。POCは、他の普及しているObjective-Cと互換性が ないため、このFAQではこれ以上取り上げません。

Objective-C 2.0とは何ですか

Appleは、Mac OS Xバージョン10.5("Leopard")において、Objective-Cに いくつかの追加の機能を導入しています。これらの機能は"Objective-C 2.0"と して登場する予定です。ここではこれをObjC 2と呼ぶことにします。ObjC 2に ついては、Appleが公式にアナウンスするときに明らかになるでしょう。 後述するObjC 2のセクションを参照してください。また、ObjC 2に関する議論の中で、 さらなる検討を必要とするところでは、このフォントスタイルを 使って記述します。今のところ、ObjC 2の機能は、OS X 10.5のデベロッパ プレビューに含まれるApple版のランタイムでのみ利用できます。新しい機能を使用する かどうかは自由に選択でき、既存のObjective-Cとの互換性は失われません。

Objective-C開発ツール

Objective-Cのソースファイルにはどのような名前をつけるのですか

Objective-Cのソースコードは、一般的に、CやC++のソースコードと同じように 構成します。宣言とオブジェクトのインタフェイスは、拡張子.hを付けたヘッダ ファイルに記述し、定義とオブジェクトの実装は、拡張子.m(メソッド[method]の略)を 付けたファイルに記述します。

どのようなコンパイラを利用できますか

ほとんどのプラットフォームで利用できるgccを Objective-Cコンパイラとして利用できます(gccには2種類ありますが、それについては 後述します)。歴史的には、NeXTコンピュータ(後のNeXTソフトウェア)とStepstoneが Objective-Cコンパイラを製作しました。しかし、どちらの会社もすでにありません。 NeXTがStepstoneからObjective-Cに関する権利を取得し、その後、AppleがNeXTの 権利を取得したことにより、現在では、Appleコンピュータが所有権を持っています。 Metrowerksの製品であるCodeWarriorという開発環境がObjective-Cコンパイラを 備えていましたが、こちらも今では存在しません。

どのような開発環境を利用できますか

Objective-Cのソースファイルは、普通のテキストエディタを使用して編集できます。 また、EmacsのようなObjective-Cの構文をハイライトするモードがあるエディタが あります。Objective-Cをサポートする統合開発環境には、Appleの Xcodeや GNUstepの ProjectCenter といったものがあります。

クラスライブラリ

クラスライブラリとは何ですか

Objective-Cコンパイラがあれば、すぐにプログラミングを始めることはできますが、 それだけでは、自分が関心を持っている対象のプログラミングをする前に、多くの 車輪の再発明をしなければならないでしょう。クラスライブラリは、事前に作成された オブジェクトとクラスのセットを提供します。クラスライブラリは通常、コンテナ、 データ処理、ユーザインタフェイスといった機能を提供します。

どのようなクラスライブラリを利用できますか

Objective-Cで利用できるクラスライブラリの多くは、NeXTコンピュータと Sunマイクロシステムズが開発したOpenStepに由来します。これらには、Apple Mac OS XのCocoa、 クロスプラットフォームのGNUstepといった ものがあります。どちらのクラスライブラリも、それぞれに特有なクラスの他に、 ファンデーション(コンテナ、文字列、ネットワーク/IPCなど)、AppKit(ユーザ インタフェイスコンポーネント)を提供します。 OpenDarwinのlibFoundationは ファンデーション部分のみ実装しています。
Swarmという エージェントベースモデリングのためのObjective-Cフレームワークがあります。

Objective-Cプリプロセッサ

#importというのはどういうものですか

#importは、C言語の#includeとよく似た動作をする プリプロセッサ指令です。#includeと異なり、#importは 同じファイルを2度インクルードしません。これにより、ヘッダファイルの中で #ifdefを用いて2重インクルードを防止する必要はなくなります。 gccのバージョンによっては、#importを使うと警告を出すものが あります。その場合には、-Wno-importオプションを指定すること で警告を出力させないようにすることができます。

Objective-Cではコメントはどう書くのですか

C言語と同じスタイルのコメントを使用できます。ひとつは、//で 始まるコメントで、行末までがコメントになります。

// BCPLスタイルのコメント

もうひとつは、/*で始め、*/で終わるコメントです。

/* Cスタイルのコメント
 * 複数行にわたってコメントできる
 * この形式のコメントはネストできないことに注意!
 */

オブジェクトとクラス

オブジェクトの型は何ですか

Objective-Cのキーワードidは、任意のオブジェクトへの参照に 相当する型です。特定のクラスのポインタを使えば、より厳格な型チェックを行わせる こともできます。例えば、MyClass *には、MyClassクラス、 または、そのサブクラスのオブジェクトへの参照を格納することができますが、その他 のクラスのオブジェクトへの参照を格納しようとするとエラーになります。

self, superとは何ですか

selfは、メソッドの実装の中で利用できる特別な変数で、現在の オブジェクト(すなわち、メソッドを起動したメッセージを受信したオブジェクト)への 参照を格納しています。 superは、スーパークラスに実装されているメソッドを起動するために 使用します。selfとsuperは一般的にオブジェクトの初期化で使用されます。

-(id)init {
  if((self=[super init])) { // スーパークラスの-initを起動する
    someIvar=[[MyClass alloc] init];
  }
  return self; // オブジェクト自身を返す
}

クラスオブジェクトとは何ですか

次のように、オブジェクトに対して-classメッセージを送信すると、 オブジェクトのクラスを表すオブジェクトを返します。

MyClass *someObject=[[MyClass alloc] init];
id aClass=[someObject class];

クラスオブジェクトはルートクラスのインスタンスのように振る舞います(すなわち、 [aClass class]はルートクラスを返します)。aClassが 参照しているオブジェクトは、クラスオブジェクトMyClassであり、 MyClassにメッセージを送信したときと同じようにクラスメッセージに 応答します。例えば、[[aClass alloc] init]MyClassの 新しいインスタンスを生成します。

nilとは何ですか

nilはnullオブジェクトを表し、オブジェクトが未初期化である ことや、クリアされた状態であることを表わすのに使用されます(また、Nilはnullクラス オブジェクトを表します)。他の言語と異なり、Objective-Cでは、nilにメッセージを 送信することが許されています。そのときの返却値は、返却値の型がid型のメッセージ の場合はnilを返し、intのような単純なC言語の型の場合は0を返します。structの ような複合型の場合の返却値は未定義です。nilとNilはともに(id)0と 定義されています。

@defsとは何ですか

@defsキーワードは、与えられたクラスのオブジェクトのメモリ上に おけるレイアウトと同じC言語の宣言のリストを用意します。これは、Objective-Cの オブジェクトを操作するCのAPIを作成するのに使用できます。次のように使用します。

struct MyClass_t {
  @defs(MyClass)
};

ライブラリの中での使用であれば問題ないのですが、一般的に@defsの 使用はカプセル化に違反することになるので、好まれないことに注意してください。

オブジェクトが特定のメッセージの送信を止めるにはどうすればよいですか

手短に言うと、それはできません。(-(id)doSomething:(id)senderの ように)メッセージのパラメータにコンテキスト情報を含めればできるかもしれ ませんが、メッセージ送信者が正確なコンテキスト情報を渡す必要があります。

クラスのインスタンス変数を変更した場合、すべて再コンパイルする必要がありますか

その必要はありません。普通は、変更したクラスとそのサブクラスを再コンパイル しなければなりません。@defs()を使用しているCのソース(あるいは インスタンス変数に直接アクセスしているソース)は、そのクラスのメモリ上の レイアウトが変わるので、再コンパイルする必要があります。インスタンス変数に アクセサメソッド、または、KVC/KVO(「キー値コーディング、キー値監視とはなん ですか」を参照)を介してアクセスしているコードは、再コンパイルする必要は ありません。

カテゴリ

カテゴリとは何ですか

カテゴリは、既存のクラスにメソッドを追加する方法です。 カテゴリは、既存の@interface@implementation宣言と は別の宣言として記述され、そこには、カテゴリが実行時にロードされるときに クラスに追加されるメソッドが含まれます。カテゴリは、gcc版のObjective-Cを 実装しているコンパイラでのみ利用できます。

カテゴリはどのように記述するのですか

カテゴリのインタフェイス宣言は次のように記述します。

@interface ClassName (CategoryName)
-method;
@end

カテゴリ名は実行時に何の影響も及ぼしませんが、同じクラスの他のカテゴリとは 異なる、一意な名前を付けなければなりません。また、カテゴリ名はスタックトレースに現れます。
カテゴリの実装は次のように記述します。

@implementation ClassName (CategoryName)
-method {...}
@end

カテゴリの目的は何ですか

カテゴリを使用すると、ひとつのクラスの実装を複数のファイルに分割することが できます。クラスの実装をカテゴリに分割し、それらを別々のファイルに記述します。 また、カテゴリを使用すると、ソースコードを直接修正できないクラスにメソッドを 追加することもできます。たとえば、あなたが使用しているクラスライブラリに 含まれるあるデータコンテナに、-md5Sumメソッドを追加するといった ようなことができます。

プロトコル

プロトコルとは何ですか

簡単に言ってしまえば、プロトコルは実装を持たないインタフェイスです。 プロトコルは便宜上ひとつにまとめられたメッセージの集合です。プロトコルを使用 すると、あるクラスがプロトコルのすべてのメッセージを実装していると宣言する ことや、あるオブジェクトがそのプロトコルに適合しているかどうかを確認すること ができます。また、プロトコルを変数の型宣言に使用して、その変数が参照する オブジェクトが特定のメソッドの集合を持つが、そのオブジェクトの実際のクラスに ついては関心がないということを示すことができます。

プロトコルはどのように宣言するのですか

プロトコルは次のように宣言します。

@protocol ProtocolName
-method;
@end

プロトコルは他のプロトコルから継承できます。プロトコルの継承は、クラスの 継承の場合と同じように、親プロトコルのメソッドが子プロトコルに追加されます。 プロトコルを継承する場合、宣言の1行目は次のようになります。

@protocol ProtocolName <ParentProtocol>

プロトコルに適合するクラスはどのように宣言するのですか

クラスのインタフェイス宣言を次のように記述します。

@interface ClassName <ProtocolName>

複数のプロトコルに適合させる場合、プロトコル名をカンマで区切って記述します。

私のクラスはあるプロトコルのメソッドをすべて実装していますが、コンパイルすると警告が出ます

デフォルトでは、次のようなクラスの場合、

@interface MyClass : NSObject <AProtocol>

gccは、AProtocolプロトコルから継承したメソッドが実装されている か調べるために、MyClassのインタフェイスだけ調べます。そのため、 そのメソッドがスーパークラスで実装されていても、MyClassにメソッド がみつからない場合は警告します。gccに-Wno-protocolオプションを 指定すると、gccはプロトコルメソッドをクラス階層の中から探すようになります。

オブジェクトがプロトコルに適合しているかどうかはどのように調べるのですか

Objectクラスの場合、次のように記述します。

[obj conformsTo:@protocol(ProtocolName)]

NSObjectクラスの場合、次のように記述します。

[obj conformsToProtocol:@protocol(ProtocolName)]

プロトコルに適合する変数はどのように宣言するのですか

プロトコルに適合する変数で、クラスに制限がないものを宣言するには、次のように 記述します。

id <ProtocolName> obj;

特定のクラス(またはそのサブクラス)で、プロトコルに適合する変数を宣言する には、次のようにします。

ClassName <ProtocolName> *obj;

この形式を使用することはほとんどありません。

「非形式プロトコル」とは何ですか

非形式プロトコルは実のところプロトコルではなく、メソッドの実装を持たない カテゴリのインタフェイス宣言です。非形式プロトコルはCocoaの中でよく使用され、 形式プロトコルの使用が適当でない状況で、コンパイラに対してメソッドの集合を 宣言するために使用されます。たとえば、実装する必要のないメソッドが多くある というような状況です。

セレクタと実装ポインタ

SELとは何ですか。どのように取得するのですか

SELは、メソッドセレクタを表わすCのデータ型です(すなわち、SEL型の データは個々のメッセージを一意に識別します)。メソッドセレクタは、@selector() キーワードを使用して取得できます。

SEL aSelector=@selector(doSomethingWithStuff:);

また、ランタイム関数を使用して取得することもできます。 Apple gccではsel_getUid()を使用し、 GNU gccではsel_get_any_uid()を使用します。

perform:performSelector:は何をするのですか

perform:performSelector:は、指定されたセレクタに 対応するメッセージをオブジェクトに送信します。すなわち、 [obj performSelector:@selector(message)]と記述するのは、 [obj message]と記述するのと同じことです。 両者の違いは、perform:performSelector:は、セレクタを 参照する変数を引数として渡すことができるのに対し、普通に[obj message]と 記述した場合には、常に同じメッセージを送信します。

IMPとは何ですか。どのように取得するのですか

IMPは、メソッドの実装を表わすCのデータ型で、実装ポインタとも 呼ばれています。IMPは、返却値がid型で、selfと メソッドセレクタ(メソッド定義の中で変数_cmdとして参照可能)を引数の最初に とる関数へのポインタです。

id (*IMP)(id, SEL, ...);

NSObjectの場合、次のようにして、指定されたメソッドのIMPを 取得できます。

IMP imp=[obj methodForSelector:@selector(message)];

Objectの場合は、次のようにします。

IMP imp=[obj methodFor:@selector(message)];

IMPで与えられたメッセージはどのように送信するのですか

Cの関数ポインタのように、間接参照(Dereference)します。

id anObject, theResult;
IMP someImp;
SEL aSelector;
// ...
theResult=someImp(anObject,aSelector);

IMPid型以外の値を返すメソッドに使えますか

IMPはid型の値を返すメソッドへのポインタです。IMPを id型以外の値を返すSELとともに使用すると問題を起すことがあるので、 IMPの代わりに新しい型を用意して、その型にキャストしたほうがよい でしょう。

int (*foo)(id,SEL);
int result;
foo=(int(*)(id,SEL))[anArray methodForSelector:@selector(count)];
result=foo(anArray,@selector(count));

Objective-Cのランタイム関数を使用してIMPを取得するには、 GNUのランタイムの場合、objc_msg_lookup(id,SEL)を使用します(これを 説明してくれたJustin Hibbitsに感謝します)。NeXT/Appleのランタイムの場合は、 class_getInstanceMethod(Class,SEL)を使用することができるでしょう。 この関数でstruct objc_methodへのポインタを取得し、 その構造体のmethod_impメンバが、あなたが必要とするIMPです。

SELid型以外の値を返すメソッドに使えますか

ランタイムのメッセージ送信関数を直接使用するか、前の質問の中で論じたのと 同じように、メソッドのIMPを取得し、それをキャストすることで可能です。

Apple/NeXTランタイムにおけるメッセージ送信関数はobjc_msgSend(id,SEL,...)です。 ただし、メソッドの返却値の型によって異なる関数が提供されており、それらを使い分ける必要が あることに注意してください。 詳細は、/usr/include/objc/objc-runtime.h、または、Objective-C Runtime Referenceを 参照してください。簡単な例を以下に示します。

        typedef int (*IntReturn)(id,SEL);
	NSArray *ary=[NSArray alloc];
	SEL initSel=@selector(initWithObjects:),countSel=@selector(count);
	// id型の値が返される
	ary=objc_msgSend(ary,initSel,@"Hello",@"World",nil);
	// int型の値が返される
	c=((IntReturn)objc_msgSend)(ary,countSel);
	NSLog(@"%@: %d",ary,c);

GNUランタイムでは、objc_msg_sendv(id,SEL,arglist_t)を使用できる でしょう。この関数は、任意のメッセージの送信に使用できますが、コーリングフレームを 作らなければならないので、objc_msgSend()よりも多くのセットアップを 必要とします。以下の例は、GNUstep DOコードをベースにしています。 例によって、メモリ管理は省略しています(コーリングフレームを解放する必要があります)。

        NSArray *ary=[[NSArray alloc] initWithObjects:@"Hello",@"World",nil];
        int *cp;
        SEL countSel=@selector(count);
        Method_t meth=class_get_instance_method([ary class],countSel);
        arglist_t frame=objc_calloc(8,1);
        const char *rettype=objc_skip_type_qualifiers(meth->method_types);
        int stackargs=atoi(objc_skip_typespec(rettype));
        frame->arg_ptr=stackargs?objc_calloc(stackargs,1):0;
        cp=(int *)objc_msg_sendv(ary,countSel,frame);
        NSLog(@"%@: %d",ary,*cp);

返却値が普通の型(構造化されていない、sizeof(t)<=sizeof(id)と なるような型 t)の場合は、前の質問で述べたように、IMPを取得し、適切な 返却値を持つ型にキャストするのが簡単です。

例外

例外とは何ですか

例外はプログラムの実行中に起こるイベントで、通常のプログラムの流れを 中断します。Javaのような他のオブジェクト指向言語を使用していたプログラマ なら、例外についてよく知っているでしょう。Objective-Cによるプログラミング ではよく、(アサーションのテストのような)単にプログラマのエラーを見つける ために使用されたり、通常のプログラムの流れの中でユーザにエラーを知らせる ために使用されます。

例外はどのように使うのですか

例外は、今のところ、NeXT gcc(「GNU gcc、Apple/NeXT gccと呼ぶのはなぜ ですか」を参照)ランタイムでのみ利用することができます。例外を使用するには、 gccの-fobjc-exceptionsオプションを有効にしなければなりません。 例外をスローする可能性のあるコードは、@tryブロックの中に記述 します。@tryの後にひとつ以上の@catchブロックを置き、 その中でスローされた例外を処理します。@catchに続く@finally ブロックには、例外が発生するしないにかかわらず実行すべきコードを記述します。 例外で使用するオブジェクトはどのようなオブジェクトでもよく、@throwキーワード を使ってスローします。以下に例を示します。

@try {
  // 例外がスローされる可能性のあるコード
  ...
}
@catch(MyExceptionClass *ex) {
  // カスタム例外を処理する
  ...
}
@catch(id *ex) {
  // 一般的な例外のハンドラ
  ...
}
@finally {
  // ここは例外が発生してもしなくても実行される
  ...
}

上記のコードには、ふたつの@catchブロックがあります。ひとつは 具体的な型の例外をキャッチし、もうひとつは一般的な型の例外をキャッチします。 最も具体的な型の例外ハンドラを先に記述する必要があります。 例外は、gotoreturnによって発生するコールスタックの 変化に脆弱であるので注意してください。

例外を再スローすることはできますか

できます。キャッチした例外を再スローするには、@catchブロックの 中で、引数なしで@throw()と記述します。

NS_DURING, NS_HANDLERといったマクロを使用した 例外処理を見たことがあります。これは一体何ですか。

gccにObjective-Cの例外の構文が導入される前には、Cocoaは独自の例外処理メカニズム を提供していました。それは、Cの標準関数であるlongjmp()setjmp() を使用したマクロをベースにしていました。Objective-C言語に導入された例外処理 (@tryなど)の実装は、今のところ、NS_DURINGなどを 使用する例外処理とバイナリ上の互換性があります。

スレッド同期

@synchronizedとは何ですか

@synchronizedは、マルチスレッドプログラミングのための単純な ロックをサポートするコンパイラ指令です。@synchronizedは、NeXTの ランタイム上で、gccの-fobjc-exceptionsフラグ(前述の「例外処理」を 参照)を指定することによって利用できます。@synchronizedは、明示的に ロックを生成、管理することなく、どのようなオブジェクトでも暗黙のうちにロック することができます。あなたがJavaをよく知っているなら、@synchronizedは Javaのsynchronizedと基本的に同じものであると考えてください。

@synchronizedはどのように使うのですか

構文は次のようになります。

@synchronized(object) {
  ...
}

このコードは"object"をロックします。他のスレッドが同じオブジェクトに 対する@synchronizedブロックを実行しようとするとき、実行中の スレッドが@synchronizedブロックの実行を終了するまで処理を 進めません。これは次のコードと基本的に同じです。

[[object getLock] lock];
...
[[object getLock] unlock];

違いは、オブジェクト自身が上記のようなメソッドを持って、明示的にロック オブジェクトを管理する必要がないことです。

スレッド同期を使うことの利点は何ですか

明示的にロックを生成、削除する必要がないので、コードの読み書きがしやすく なります。また、@synchronizedはreturn文と例外を認識するので、 ブロックの途中で処理を中断しても、ロックは必ず解放されます。

スレッド同期を使うことの欠点は何ですか

@synchronizedは、暗黙のロックを検索するオーバーヘッドが大きい ので、明示的なロックに比べて処理が遅いです。また、@synchronizedは 常に再帰的なロックを使用します。再帰的なロックはそうでないロックに比べて処理が 遅いです。さらに、条件変数やその他の高度なスレッドの概念をサポートしていない ので、柔軟性がありません。

gcc Objective-Cコンパイラ

GNU gcc, Apple/NeXT gccと呼ぶのはなぜですか

Apple(以前はNeXT)が使用しているgccのバージョンは、Free Software Foundationが 配布しているgccとは異なるObjective-Cの実装(特にランタイムが異なる)を提供します。 DarwinとMac OS Xは、Apple gccとともに出荷されています。CocoaはApple gccに 依存しています。他のオペレーティングシステムは、一般的に、GNUのObjective-Cを 使用しています。GNUstepは、GNUのObjective-Cを必要とします。 しかし、どちらのコンパイラのソースも利用できるので、どちらでも使用したい方を あなたが使用しているプラットフォーム用にビルドできます。 Appleは独自のgccを使用しているため、両者で利用可能な機能はいつでも異なる 可能性があります。あなたが使用しているgccのバージョンのドキュメントを調べて、 利用したい機能が実際に存在するか確かめてください。

.mファイルはどのようにコンパイルするのですか

クラスライブラリを使用していない場合は、次のようにします。

gcc -c myfile.m -lobjc

クラスライブラリを使用している場合は、そのクラスライブラリのドキュメントを 調べてみてください。たとえば、Cocoaを使用する場合は、次のようにします。

gcc -framework Cocoa -c myfile.m

Objective-C++とは何ですか

gccは、Objective-CとC++を混在させることができるObjective-C++と呼ばれるものを サポートしています。Objective-C++のコードは、拡張子.mmを付けたファイルの中に 記述します。Objective-C++では、Objective-CのオブジェクトをC++のオブジェクトの インスタンス変数に保持でき、またその逆もできます。しかし、C++のクラスを 継承してObjective-Cのクラスを定義することはできず、その逆もできません。 Objective-CとC++の例外処理メカニズムは完全に異なり、C++のcatchを 使用して、@throwで発生させた例外をキャッチすることはできません。

文字列定数クラスとは何ですか

@"Hello world!"のようなObjective-Cの文字列リテラルを 使用する場合、その文字列オブジェクトのクラスは通常NXConstantString になります。この文字列クラスは、コンパイル時に gccの-fconstant-string-classオプションを使用して変更することが できます。Cocoaでは、文字列リテラルのクラスはNSConstantStringに なります。もし、文字列定数クラスを別のものに変更したいなら、そのクラスには どのようなメソッドを実装してもよいですが、インスタンス変数のレイアウトは、 メモリ上のレイアウトをコンパイラが実行ファイルに書き込むので、 N[SX]ConstantStringと同じにしなければなりません (objc/NXConstStr.h、または、Foundation/NSString.hを参照)。

Objective-C 2.0の機能

新しいfor構文とはどのようなものですか

NSArray *bar=...;
for (id foo in bar)
{
  [foo doSomething];
}

上記のコードは、barの中のすべてのオブジェクトに-doSomething メッセージを送信します。この点において、新しいfor構文は、よく 知られたオブジェクトの列挙と発想は同じです。

NSArray *bar=...;
NSEnumerator *e=[bar objectEnumerator];
id foo;
while(foo=[e nextObject])
{
  [foo doSomething];
}

実際には両者の実装は異なりますが、特に大きなコレクションに対しては、新しい 構文の方がより早く処理できます。また、列挙中にコレクションが変更されることも 防ぎます。

CocoaとGNUstep

ルートクラスはどのようにインクルードするのですか

#import <Foundation/NSObject.h>

完全を期して言えば、NSProxyもルートクラスです。

オブジェクト間でメッセージを転送するにはどうすればよいですか

オブジェクトが応答できないメッセージを受信した場合、ランタイムはエラーを 発生させる前に、そのオブジェクトに対して-forwardInvocation:メッセージを 送信します。これを利用して、あなたのクラスで メソッド-(void)forwardInvocation:(NSInvocation *)invを オーバーライドすることにより、認識できないメッセージを他のオブジェクトに 転送することができます。詳細は、developer.apple.comの「転送」の章を参照してください。また、メソッド -(NSMethodSignature *)methodSignatureForSelector:(SEL)selectorも オーバーライドする必要があります。そうしないと、このメソッドがnilを 返してforwardInvocation:が呼び出されません。

メッセージ転送の簡単な例として、コレクションを操作するループをオブジェクトの 内部に持たせる方法、すなわち、コレクションの各要素に対してメッセージを送信する 手段をコレクションに持たせる方法を以下に示します。 まずはじめに、トランポリンクラスのCollectionTrampolineを用意 します。このクラスは、受信したメッセージをコレクションの各要素に転送します。 以下に、このクラスのインタフェイスを示します。

@interface CollectionTrampoline : NSProxy
{
	id theCollection;
}
- (id)initOnCollection:(id)aCollection;
@end

以下は、オブジェクトの生成と解放メソッドです。

- (id)initOnCollection:(id)aCollection
{
	theCollection=[aCollection retain];
	return self;
}

- (void)dealloc
{
	[theCollection release];
	[super dealloc];
}

前述したように、メッセージを転送するには、methodSignatureForSelector:を オーバーライドする必要があります。メソッドシグニチャを生成するAPIはないので、 ここではコレクションの最初のオブジェクトからメソッドシグニシャを探します。 もしみつからなければ、デフォルトのメソッドシグニチャを提供します。 これはあまり問題になりません。なぜなら、まだシグニチャを取得していないなら、 コレクションは空で転送が起らないか、最初のオブジェクトがメソッドを実装して いないので、何か処理する前に例外が発生するからです。この例を提供してくれた Michael Ashに感謝します。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
  NSMethodSignature *sig = nil;

  if([theCollection count])
    sig = [[theCollection objectAtIndex:0] methodSignatureForSelector:aSelector];

  if(!sig)
    sig = [NSObject instanceMethodSignatureForSelector:@selector(release)];

   return sig;
}

次はforwardInvocation:の実装です。NSEnumeratorを 使用してコレクションのオブジェクトをイテレートし、各オブジェクトのメソッドを 起動します。この簡単な例では、オブジェクトからの返却値は考慮しません。完全な 例をあげるとすれば、応答のコレクションを生成して、それを戻すことになるでしょう。

- (void)forwardInvocation:(NSInvocation *)anInv
{
	id i;
	NSEnumerator *en=[theCollection objectEnumerator];
	
	while(i=[en nextObject])
	{
		[anInv invokeWithTarget:i];
	}
}

最後に、コレクションクラスがトランポリンオブジェクトを返却する方法を用意 します。これを行うNSArrayのカテゴリを以下に示します。

@interface NSArray (Trampolines)
- (id)do;
@end

@implementation NSArray (Trampolines)
- (id)do
{
	return [[[CollectionTrampoline alloc] initOnCollection:self] autorelease];
}
@end

これで、このトランポリンを使用して、コレクション中のすべてのオブジェクトに メッセージを送信することができます。

[[anArray do] doSomething];

リモートオブジェクト間でメッセージを転送するにはどうすればよいですか

これは、CocoaとGNUstepで分散オブジェクトまたはDOと呼ばれるものです。 DOを使用すると、異なるプロセスや同じプロセス中の異なるスレッドのオブジェクトに 対してメッセージを送信できるだけでなく、異なるシステム上で動作するプロセスの オブジェクトに対してもメッセージを送信することができます。 サーバアプリケーションは、ネームサービスにオブジェクトを登録することによって、 オブジェクトをベンド(vend)します。クライアントアプリケーションは、ネームサービス に問い合わせ、ベンドされたオブジェクトのプロキシを取得し、これを介してベンド されたオブジェクトに接続します。DOは透過的です。プロキシに送信されたメッセージは リモートオブジェクトに送信され、その結果がプロキシに返されるので、メッセージを 送信した側からみれば、ローカルオブジェクトがメソッドを実行しているかのように みえます。DOの簡単な使い方の例をコメント付きで以下に示します。 詳細は、 Apple DO guide、または、 GNUstep DO tutorial を参照してください。この例では、networked DOは使用していません。また、エラー 処理も省いています。

DOprotocol.h

このプロトコルは、サーバとクライアントの双方にインクルードされます。 ここでは、ベンドされたオブジェクトが応答できるメッセージを記述しています。 この例では、メッセージをひとつだけ記述しています。

@protocol DOExampleProtocol
-(NSNumber *)myPid;
@end

DOserver.m

サーバプロセスです。

#import <Cocoa/Cocoa.h>
#import "DOprotocol.h"
#import <sys/types.h>
#import <unistd.h>

@interface VendedObject : NSObject <DOExampleProtocol>{
}
-(NSNumber *)myPid;
@end

@implementation VendedObject
-(NSNumber *)myPid
{
	NSLog(@"Vending out my pid...");
	return [NSNumber numberWithInt:getpid()];
}
@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	VendedObject *vo=[[VendedObject alloc] init];

        // デフォルトのDOコネクションを取得する
	NSConnection *conn=[NSConnection defaultConnection];
	BOOL status;

        // ベンドするオブジェクトを設定し、ネームサーバに登録する
	[conn setRootObject:vo];
	status=[conn registerName:@"VendingServer"];
	if(status==NO)
	{
		NSLog(@"Couldn't register as VendingServer");
		exit(EXIT_FAILURE);
	}

        // コネクションを待つ
	[[NSRunLoop currentRunLoop] run];
	
    [pool release];
    return 0;
}

DOclient.m

このクライアントは、適切な名前を持つDOサーバをローカルマシンのみから 探します。そして、リモートプロセスのpidを取得します。

#import <Foundation/Foundation.h>
#import <stdlib.h>
#import "DOprotocol.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	
	// コンパイラの型チェックのためにプロトコルを指定する
	id <DOExampleProtocol> clientObject;
	NSNumber *serverPid;
	
	/* 適切な名前を持つサーバを探し、そのサーバからプロキシを取得する。
	 * ホストにnilを指定するとlocalhostのみから探す。
	 */
	clientObject=(id <DOExampleProtocol>)[NSConnection rootProxyForConnectionWithRegisteredName:@"VendingServer" host:nil];
	/* -setProtocolForProxy:は、DOメカニズムに対して、リモートオブジェクトが
	 * 指定されたプロトコルに適合していることを伝える。これにより、プロトコルで
	 * 宣言されているメッセージは、送信する前にチェックされない。
	 * プロトコルで宣言されていない他のメッセージはこれまで通り送信される。
	 */
	[clientObject setProtocolForProxy:@protocol(DOExampleProtocol)];
	
	if(clientObject==nil)
	{
		NSLog(@"Error: did not get a proxy object for VendingServer service");
		exit(EXIT_FAILURE);
	}
	serverPid=[clientObject myPid];
	
	if(serverPid!=nil)
	{
		NSLog(@"Remote server on pid %@",serverPid);
	}
	else
	{
		NSLog(@"Error, did not get the server's pid");
		exit(EXIT_FAILURE);
	}
    [pool release];
    return 0;
}

リファレンスカウンタ方式はどのような仕組みですか

今のところ、gccのObjective-Cには自動ガベージコレクションはありませんが、 リファレンスカウンタ方式を使用すれば、手動でメモリ管理を行うよりは作業量は 少なくなります。NSObjectから派生したオブジェクトは、1から始まるリファレンス カウンタをはじめから備えています。オブジェクトを参照している間に解放され ないようにするには、そのオブジェクトに-retainメッセージを送信 します。不要になったらそのオブジェクトに-releaseメッセージを 送信します。リファレンスカウンタが0になるまでオブジェクトは解放されません。

リファレンスカウンタ方式に関するすぐれた解説が Hold Me, Use Me, Free Me にあります。

キー値コーディング、キー値監視とは何ですか

Cocoaは、キー値コーディング(KVC)とキー値監視(KVO)という、オブジェクトの プロパティの検査と変更を間接的に行う方法を用意しています。 KVCとKVOは主に、Cocoaバインディングの中で、アプリケーションのユーザインタフェイスと データモデルの間の同期を効率的に行う方法として使用されています。

オブジェクトmyObject-someVariable, -setSomeVariableの ようなアクセサメソッドか、someVariable, _someVariableと いったid型のインスタンス変数がある場合、次のようなコード でsomeVariableの値に(継承されたプロパティであっても)アクセスできます。

[myObject valueForKeyPath:@"someVariable"];
[myObject setValue:@"Hello" forKeyPath:@"someVariable"];

他のオブジェクト、たとえばmyOtherObjectは、someVariableの値が 変更されたとき、KVOによって通知を受けることができます。

[myObject addObserver:myOtherObject forKeyPath:@"someVariable" options:NSKeyValueObservingOptionNew context:NULL];

これにより、someVariableが変更されたとき、myOtherObject-observerValueForKeyPath:ofObject:change:context:メッセージを受信します。

詳細は、 the KVO Programming Guide the KVC programming guideを参照してください。

文献案内

このFAQの中で取り上げた参考文献の他にも、以下の文献が役に立つでしょう。

The Objective-C Programming Language - Objective-Cに関するAppleの公式なドキュメント

Duncan, A.M., Objective-C Pocket Reference, O'Reilly Media (ISBN: 0-596-00423-0). GNUとNeXT/Appleの両方のObjective-Cを取り上げたリファレンス

もし、C言語(Objective-Cの基盤となる言語)でプログラミングをした経験がないなら、 次の書籍を読んでみるとよいでしょう。
Kochan, S.G., Programming in Objective-C, Sams Publishing (ISBN: 0-672-32586-1)
ただし、いくつか注意事項があります。この書籍ではまず、Objectルートクラスを 使用してObjective-Cのテクニックを紹介し、次に、第2章でFoundationについて 論じるときには、NSObjectクラスを使用します。これらのクラスでは、 いくつかのセレクタの名前(たとえば、protocol conformance)が 異なるので、読者は混乱するかもしれません。 また、GNUstepを使用するときには、ビルド時のディレクトリ構造がデフォルトで 「フラット(flattened)」であるので、313ページに掲載されているシェルへの入力の 1行目にある"cd shared_obj/ix86/mingw32/gnu-gnu-gnu"を "cd shared_obj"に変更する必要があります。

公式FAQの c.l.o-c FAQ では、参考文献として、オブジェクト指向プログラミングに関する書籍を何冊か 紹介していますが、そこで紹介されている文献の多くが絶版になっているので 注意してください。 そしてもちろん、comp.lang.objective-cに 質問することもできますし、Googleグループで 記事のアーカイブを調べることもできます。 Appleは、 ObjC-language メーリングリストを主催しています。