いよいよ、コーディングです!いやー。長かったですね。でも、これまでやってきたことは、理解してしまえば一瞬で終わることです。Cocoaはそれだけ面倒見が良く、メインの処理に集中できるというわけです。
なお、今回のコーディングは、前回立てたプログラムの方針を参照しながら見て下さい。
まずはインスタンス変数の宣言を行うことにします。MyDocument.hを開いて下さい。雛形では、次のようになっています。
@interface MyDocument : NSDocument { } @end
Objective-Cのヘッダは次の様に書きます。
@interface クラスの名前:スーパークラスの名前 { インスタンス変数の宣言 } メソッドの宣言 @end
スーパークラスがNSDocumentですから、MyDocumentはNSDocumentを継承したクラスだということです(今までオブジェクト指向プログラムの経験が無くて、継承という言葉を聞いたことが無い方は、第5回で紹介しました「Mac OS Xプログラミング入門 Objective-C」を、第3章まで読んでおくことをお勧めします。)。では、NSImageのインスタンスへのポインタの宣言を追加しましょう。次の様に1行書き加えて下さい(3行目が書き加えられた行です。)。
@interface MyDocument : NSDocument { NSImage *image; } @end
これで、imageのポインタを格納する場所が出来ました。なお、Objective-Cでは、インスタンス化される時にインスタンス変数は全て0で初期化されますので、それでかまわない場合は、特別な初期化処理は必要ありません。
続きまして、loadDataRepresentation:ofType:に、NSDataからNSImageへの展開を実装します。次の様に1行書き加えて、returnの行は修正して下さい(3行目が書き加えられた行で、4行目が修正した行です。)。
- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType { image = [ [ NSImage alloc] initWithData:data]; return ( image != nil)?( YES):( NO); }
Objective-Cは、Objective-Cで拡張された部分以外はC言語とまったく同じですから、馴染みが無い書き方をしているのは3行目の[]で囲われた部分だけだと思います(1行目のメソッドの宣言の部分は、既に第6回で説明しました。)。そこでまずは、この部分について説明します。
Objective-Cでは、オブジェクトにメッセージを送る時、次のように記述します。
[ object message]
このオブジェクトは、メッセージを受け取るので、レシーバーと呼ばれます。レシーバーにメッセージを送ると、レシーバーでメッセージと同じ名前のメソッドが実行されます。そして、C言語同様、この式はその実行結果の値になります。それでは順番に見て行きます。
[NSImage alloc]
これは、NSImageクラスに対してallocというメッセージを送っています。C++をやってこられた方は、クラスに対してメッセージを送るということを奇妙に感じるかもしれませんが、Objective-Cではクラスも実体を持っています。NSImageのリファレンスを開いて下さい(場所は前回の説明を見て下さい。NSImageはApplication Kitの中にあります。)。Inherits from:NSObjectと書いてありますから、NSImageはNSObjectを継承しています。そこでNSObjectのリファレンスを開いて下さい(通常は、このNSObjectと書いてある所をクリックすれば開くはずですが、今試してみたらリンクが切れています。しょうがないので、Foundationの一覧の中から開いて下さい。)。Method Typesの中にallocの記述があります(見つからなかったら、検索すると良いと思います。)。
+ alloc
今までのメソッドと一つ違うのは、先頭が+で書いてあることです。この+で書いてあるメソッドをクラスメソッドと言い、最初から実体があります。allocは、レシーバーをインスタンス化するメソッドです。つまり、[NSImage alloc]は、NSImageクラスをインスタンス化して、そのインスタンスオブジェクトを返します。さて、3行目はこの式だけで終わっていません。この結果に対して、さらにメッセージを送っています。
[ [ NSImage alloc] initWithData:data]
[NSImage alloc]がインスタンスオブジェクトを返しますから、そのインスタンスオブジェクトに対して、initWithData:dataというメッセージを送っています。NSImageのリファレンスを読めば分かりますが、これはdataで初期化せよというメッセージです。Objective-Cでは、インスタンス化した後、必ず初期化しなければなりません。通常は次の様に書きます。
[ [ Class alloc] init]
ただし、クラスによっては指定イニシャライザが決められていて、それで初期化しなければならないものもあります。NSImageもその一つで、 initWithData:は指定イニシャライザの一つです。NSImageのリファレンスでは、Initializing a new NSImage instanceに指定イニシャライザがまとめられています。
初期化が終わりましたら、後はそれをインスタンス変数のimageに格納します。
image = [ [ NSImage alloc] initWithData:data];
NSImageクラスは、 initWithData:でJPEGファイルの内容を渡して初期化すると、そのJPEGファイルを展開しますので、これで望む処理が出来たことになります。でも、もしこの初期化に失敗した場合は、どうなるのでしょうか。その場合は、 initWithData:がインスタンスを解放してnilを返しますので、imageはnilになります。従いまして、loadDataRepresentation:ofType:の処理が成功したかどうかを返すには、次の様にimageがnilかどうかを判断して、その結果を返せば良いわけです。
return ( image != nil)?( YES):( NO);
ところで、NSImageのインスタンス化に失敗した場合はどうなるのでしょうか?この場合はnilが返りますので、次の式はnilに対してメッセージを投げることになってしまいます。
[ [ NSImage alloc] initWithData:data]
実はObjective-Cでは、nilにメッセージを投げてもかまいません。その場合、結果もnilになります。従いまして、何らプログラム上問題は無いわけです。
プログラムはほんの数行ですが、言語の説明と同時に行っているので、随分と手間を食ってしまいました。続きは次回とさせていただきます。