だんだん好きになってきた
Objective-Cを学習中だが、最初に覚悟した割にはアウェイ感を感じていない。
@interfaceと書いて実際にはクラスの宣言だったり、その後のブレース"{}"で括った中にはインスタンス変数しか書けなかったり、「インタフェース」のことを「プロトコル」と言ったり※1、パラメタや戻り値の型を括弧"()"で括ったりと、JavaやC#に慣れた私にとっては気持ち悪さ満載な言語なのだが、その気持ち悪さにも慣れてこの言語が好きになりつつあるのである。
メッセージ式
他の言語と比べて最も特徴的で一見「キモい」のがこのメッセージ式である。
[[[Foo alloc] init] foobar];
最初こそ「キモ」かったが、慣れるとインラインでメソッドを書くよりもずっと入れ子が分かり易くなる。
Xcodeでは最初からブラケット"[" "]"の数を合わせる必要は無く、最後に閉じた所で先頭を補完してくれるのも良い。
デリゲート
Objective-CのデリゲートはC#のそれと概念的には同じである。(.NET Frameworkでの開発経験があるプログラマは喜ぶべきだろう。)
ただし、Objective-Cは概念としてのデリゲートがプロトコルやカテゴリによって実装されており、C#のデリゲートのように型として 言語での既定があるわけでは無い。
NSWindow *mainWindow = 〜
〜
[mainWindow setDelegate: self];
このように記述することでselfつまりこのクラスはNSWindowDelegateデリゲートで既定されているメッセージが送られてくるようになるので、メッセージに反応する処理を記述することができるようになる。
例) Windowがクローズする直前に実行される処理を書く
- (BOOL)windowShouldClose:(id)sender { NSLog(@"Windowは間もなく閉じられます"); return YES; }
考え方としては関数ポインタやコールバック関数のOO(オブジェクト指向)版だが、ランタイムにオブジェクトの処理をすげ替えることで型を拡張することができるため、非常に柔軟性、拡張性と富んだプログラミングを実現できる。
以前にも書いたことがあるが実装を拡張するのにオブジェクト指向の継承に頼らないのは、ソースコードが公開されていないライブラリィを拡張するために非常に重要な機能である。
Objective-Cのデリゲートで特に優れていると思うのは、非形式プロトコル(後述するカテゴリ)で実装された緩いデリゲート。これはデリゲートを受け入れることになっても、Javaのインタフェース等とは違い全てのメソッドを実装する必要が無い。(反応したいメソッドだけ書けば良い) あくまでメッセージとして適合する場合の処理だけを書けば良いのは非常に理にかなっている。※2
カテゴリ
Objective-Cの「Objective」な部分で私が個人的には最も気に入っている機能がカテゴリである。
考え方としてはRubyのモンキーパッチ※3やjavascriptのプロトタイプ、C#3.0以降の拡張メソッド等と似ており既存の型を拡張する手段として使用する。(パーシャルタイプの手段としても使えるが今はどうでも良い)
@interface NSObject (Serialize) - (void)serializeToFile:(NSString*)path; - (void)deSerializeFromFile:(NSString*)path; @end 〜 id foo = [[Foobar alloc] init]; [foo serializeToFile:@"foo.ser"];
以上、比較的設計が古い言語(1983)なので、最近の言語に比べると動的な記述やメタな記述は大してできないと諦めて学習を始めたのだが、大幅に良い方向に裏切られたと感じている。
※1:全てのメソッドの実装を必ずしも必須とする必要が無いということで、厳密にはJavaやC#のインタフェースとは違う
※2:Objective-Cのメッセージ式も対象がnilの場合は例外などは発生せず何もしないだけである
※3:カテゴリはRubyのように自由に既存の型を拡張することができる訳ではなく、いくつか重要な制限がある