Cocoaでいこう! Macらしく 第8回
Yoshiki(DreamField)
この記事は、MOSAが発行するデベロッパ向けのデジタルマガジンMOSADeN 第67号(2003年5月27日発行)に掲載された記事です。2〜3ヶ月遅れで、ここに掲載して行きます。

いよいよ、コーディングです!いやー。長かったですね。でも、これまでやってきたことは、理解してしまえば一瞬で終わることです。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になります。従いまして、何らプログラム上問題は無いわけです。

プログラムはほんの数行ですが、言語の説明と同時に行っているので、随分と手間を食ってしまいました。続きは次回とさせていただきます。

前頁目次次頁