Objective-C
![]() |
パラダイム |
オブジェクト指向プログラミング、マルチパラダイムプログラミング、クラスベース、リフレクション ![]() |
---|---|
登場時期 | 1984年 |
設計者 | ブラッド・コックス |
最新リリース | 2.0 / |
型付け | 静的型付け、動的型付け |
主な処理系 | Apple版、GNU版 |
影響を受けた言語 |
C言語、Smalltalk ![]() |
影響を与えた言語 | Java、Swift、Objective-J、Groovy、Nu |
プラットフォーム | macOS、GNUstep他 |
ウェブサイト |
developer |
拡張子 |
h、m、mm、C ![]() |
概要
@
で始まるコンパイラディレクティブで明示され、オブジェクトのメソッド呼び出しは[
]
で囲まれたメッセージ式で行われる。
最大の特徴はオブジェクトシステムが完全に動的という点で、実行時のクラス拡張、オブジェクト汎用型idの導入により型によらない動的配列・辞書など、インタプリタに近い記述力をもつことである。実際にコードそのものはネイティブコンパイルされるものの、動作原理はほぼインタプリタに近く、コンパイラ型言語としてはまれな柔軟性を発揮する。
したがって、C側から見れば一種のスクリプトインタプリタが乗っているような状態であり、逆にオブジェクトシステムからはOS機能や膨大なC言語資源を直接利用可能なインターフェースが備わっているといえる。また仮想マシンを持たずに済むため、取り回しも良い。パフォーマンスはJavaのような中間コード型言語よりも良好で、CやC++のようなネイティブコンパイル言語には劣るとされる。Objective-C特有のこの形態は双方のメリット・デメリットが明確で、実際的な使い勝手が非常に優れている。この特性に着目したのがNEXTSTEPで、UNIXとの互換性と先進的なオブジェクト指向環境の両立に成功し、その後のOS設計に大きな影響を与えることとなった。
後続言語への影響としては、特にJavaの基礎設計にその姿を見ることができる︵サン・マイクロシステムズがOPENSTEP開発を共同で行なっていたことと関係がある[1]︶。
クラス | 単一継承+インタフェース多重継承(プロトコル) 通常はルートクラスから継承 |
---|---|
オブジェクトシステム | 動的束縛、メタクラスを持つ |
型 | 動的型+見た目の静的型のハイブリッド |
実行速度 | コードはCと同等のネイティブコンパイル、メソッド呼び出しは動的ディスパッチを行なうのでやや遅延する。平均してC/C++より多少遅く、中間コード型言語(Javaなど)より数倍程度高速といわれる。ただし、クリティカルな部分はいつでもCで書き直せるため、実行速度が問題になることはまずない。 |
その他 | オブジェクトはポインタ互換、Cのスカラー型はオブジェクトではない |
歴史
基本的な構文
メッセージ送信
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
である。ただメモリ管理の一貫性などの理由から、ほとんどの場合アクセサを用いる。
互換性
#i
mport<Foundation/NSObject.h>
を記述し、ObjectクラスをNSObjectクラスに変更する必要がある。また、ライブラリの指定も従来の-lobjc
だけでは足りず、-lg
nustep-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];
...
}
}
カテゴリ
カテゴリの例
@interface NSObject (BetterHash)
- (unsigned)hash;
@end
@implementation NSObject (BetterHash)
// 子孫クラスのうち、独自のオーバーライドのない-hashは全てこの実装に置き換わる
- (unsigned)hash
{
return better_hash_function(self);
}
@end
メモリ管理
autoreleas
e
メッセージを用いて登録する。登録した全てのオブジェクトが不要になったらrelease
メッセージでNSAutoreleasePoolのインスタンスを解放する。すると登録されていたオブジェクトもすべて一斉に解放されるというものである[3]。
ほかにもオブジェクト(仮にobjA
とする)のイニシャライザ(初期化メソッド)を呼び出すと、その時点で自分をautorelease
するオブジェクトを定義できる。そのようなオブジェクトをインスタンス化したユーザは、objA
に対して明示的にautorelease
せずとも、NSAutoreleasePoolのインスタンスをre
lease
するだけで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が解放されたので1
count = [fooBar retainCount]; // Barをretainしていないのでエラー
count = [objBaz retainCount]; // ほかの参照がなかったので解放済み。エラー
count = [objBar retainCount]; // fooBarがretainしていなかったのでエラー
reta
in
または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) 方式を開発した。またガベージコレクションはOS X 10.11を最後に廃止[4]されており、それ以降Apple系ではARCが主流となっている。
自動参照カウント (ARC)
retain
/release
/aut
orelease
と同様のメカニズムで動作するが、コンパイル時にメソッドの命名ルール等を見て自動的にretain
/release
相当のコードを挿入する方式である。これにより、自動参照カウントでは明示的なretain
/release
がそもそも不可能になる。管理周りのコードの削減に加え、autorelease
管理の実行効率が向上するため、旧方式のプログラムを自動参照カウントに切り替えるだけでもパフォーマンスがいくぶん向上する。
その他
メタ言語的メカニズム
オブジェクトシステムは動的ディスパッチを行い、オブジェクトシステム自体がCで書かれていることに加え、C哲学である「プログラマにできることを制限しない」を良くも悪くも受け継いでいるため、Objective-Cにはさまざまな超言語的技法が存在している。これらの機能は非常に強力であるため乱用を避けるべきだが、この柔軟性こそがObjective-Cの魅力と評する向きもある。
- posing
- クラステーブルを書き換えることでクラスの実体を置き換える技法。
- method swizzling
- クラスのメソッドテーブルを書き換えることでセレクタ名のリネーミングを行なう技法。
- IMP呼びだし
- 毎回メッセージパッシングを行なう代わりに、一度だけメソッドを解決して関数ポインタを取り出し、メソッドの呼び出しを高速化する技法。呼びだしコストが通常のC関数と等しくなる。俗にインライン化とも呼ばれる。
Objective-C++
言語ブリッジ
処理系の特性
- Apple/NeXT版
- GNU版
- Portable Objective-C版
![]() | この節の加筆が望まれています。 |
Objective-C 2.0
脚注
参考文献
- An Introduction to Object-Oriented Programming; Timothy Budd; Addison-Wesley Publishing Company, Reading, Massachusetts; ISBN 0-201-54709-0
(邦題:「オブジェクト指向プログラミング入門」 ISBN 4-8101-8048-4)
外部リンク
- The Objective-C Programming Language(日本語訳)Apple Inc. - Objective-C言語とはどういうものかを知ることができる文書。
- ダイナミックObjective-C - マイナビニュースのコラム。