1月2008

翻訳が活発化してきてる気がする

最近のAppleはドキュメントの翻訳にも熱心になってきた様子。これでCocoaプログラマが増える事を期待しています。

Cocoaアプリケーションチュートリアル

さて、最近翻訳されたリンク先のチュートリアルですが、ざっと見た感じでもかなり良質な内容と見受けられます。チュートリアルなので、初めてCocoaアプリケーションを作成する人向けに書かれており、XcodeやInterface Builderなどの各ツールの使い方から丁寧に説明されています。また、このドキュメントのすばらしいところはチュートリアルに加えて、Cocoaのデザインパターンや大まかな仕組みなども同時に学べる点です。Cocoaをこれから初めようと思っている方には、まさに必見のドキュメントと言えるでしょう。

さらにこのチュートリアル、最新の開発環境をもとに作られているようで、Interface Builder 3.0使い方も書かれているし、Objective-C 2.0のプロパティも積極的に利用しています。新しい開発環境を確認する意味でも、従来のCocoaプログラマにも価値あるドキュメントとなる事でしょう。

仮想関数の存在意義

C++にはもちろんのこと、ActiveBasicにも仮想関数というものがありますが、これはなかなか理解し難いもので、特にObjective-Cなどの動的な言語を使っている人にとっては、馴染みのない言葉かと思われます。

仮想関数を説明するために、まずは動的型付け言語であるObjective-Cのメソッドの呼び出しの仕組みを説明しましょう。Objective-Cでは、オブジェクトにメソッドが送信された際、そのオブジェクトがメソッドを実行します。当たり前の事ですね。

[anObject methodA];

このとき何が起こっているのかを簡単に説明すると、anObjectは、自分がmethodAというメソッドが実装されているか探します。もし実装されているのならばそれを実行し、見つからなかった場合、カテゴリやスーパークラスなど、他に実装がないか探しに行きます。それで見つかれば、スーパークラスなどで実装されているメソッドが実行されますが、もしなかった場合は何も起こりません。

対して静的型付け言語であるC++は、上記のメソッドを探索する行動を、基本的には実行時ではなくてコンパイル時に行います。どういう事かと言えば、実は、オブジェクトのメソッドを探索するのは、実行時ではなくてもできるということです。例えば、次のようなコードがあります。

Dim anObject As Object
anObject.ToString

コンパイル時に、ObjectクラスがToStringクラスを実装しているかを判断する事は当然可能であり、さらにToStringを実行するためには、どこを実行するかもわかります。anObjectはObjectクラスであり、ObjectクラスのToStringメソッドを実行すると確定できる訳です。つまり、コンパイル時にメソッドを探索し、生成される実行ファイルでは、すでにどのメソッドを、どこで実行するか決まっているのです。こうなると、実行時では既に決められた道を進んでいくだけです。対するObjective-Cのような動的言語は、実行時に道を判断しながら進んでいくのです。

しかしながら、静的型付け言語であるC++などの、コンパイル時にメソッドを決定してしまう方法には、思わぬ落とし穴があります。例えば次のようなコードを見てみましょう。

Dim aString As String
Dim anObject As Object
anObject = aString
anObject.ToString

先のような、コンパイル時のメソッド決定をしてみましょう。anObjectはObjectクラスで定義されているので、anObject.ToStringは、ObjectクラスのToStringメソッドを実行するようになります。しかし、そのような動作を私たちは期待していないことは明白です。実行時、anObjectにはStringクラスであるaStringのオブジェクトが入っているので、anObject.ToStringの部分では、ObjectクラスのToString実行してほしいのではなく、StringクラスのToStringが実行されてほしい訳です。

これの解決策が仮想関数となるわけです。メソッドを宣言する際、Virtual修飾子をつける事によって、そのメソッドは実行時に探索するということをコンパイラに知らせます。つまり、仮想関数は、Objective-Cのような動的型付け言語と同じようにメソッドを探索させるものなのです。

仮想関数を使わなければならないような静的型付け言語では、メソッドを定義する際、常にこのことを頭の隅に置いてなければ、思わぬバグを引き起こしてしまいます。そう考えると、使い分ける必要のない動的型付け言語の方が断然楽ではあります。しかし、実行時のメソッド探索には当然時間がかかる訳で、コンパイル時にメソッドを決定する静的型付け言語の方が、実行速度のパフォーマンスがよい事は明らかです。

ところで、わざわざVirtual修飾子があるということは、サブクラスでオーバーライドされるメソッドが、Virtual修飾なしでも使う事ができる訳なのですが、これはどういう使い方をすればいいのでしょう?もしVirtual修飾なしの使い方がなければ、Virtual修飾子は不要という事になってしまいます。コンパイラ側が自動的にオーバーライドされてるか判断して、そのメソッドだけ自動的に仮想関数にすることができるはずですからね。

ひとりごと

最近Cocoaばっかりやっていたせいで、3月頃から再会しようと思っているActiveBasicのライブラリ開発に、すぐに手を付けられるか心配になってきます。Cocoaは非常に高レベルに位置しているフレームワークなので、Win32APIの比較的低レベルなAPIをまた使うとなると、同じプログラミングでありながらまるで別世界です。

それと最近、私はようやくCocoaバインディングを自由に扱えるようになってきて、かなり楽に、そして素早く簡単なアプリケーションを作成する事ができるようになってきました。ちょっと調べてみると、これとほぼ同等の仕組みが.NET FrameworkのFormsにもあるようで、時間があったらもう少し調べてみようかと思っています。

NSURLCache

ここ数日忙しくて久しぶりの更新ですが、しばらくこの状態が続くので、楽しみにしてくれている方がいらっしゃるのなら、更新が滞ってしまって残念です。

CocoaでのWEBページを取得する方法はいくつかありますが、最も簡単な取得の仕方はNSStringやNSDataのstringWithContentsOfURL:やdataWithContentsOfURLを使う方法です。これはメソッドひとつで手軽にWEBページを取得できます。

NSURL *URL = [NSURL URLWithString:@"http://www.lifeaether.com/overtaker/blog/"];
NSString *string = [NSString stringWithContentsOfURL:URL];
NSLog( [string description] );

もうひとつは、NSURLDownloadやNSURLConnectionを使用する方法です。上との違いは、これらはダウンロード時の動作をデリゲートで受ける事ができ、細かな設定も行える点です。NSURLDownloadはファイルに保存するクラスですが、NSURLConnectionはNSDataがダウンロードされるごとに、デリゲートで取得されるのが特徴です。

そして今回のタイトルである、NSURLChacheというのは、名前の通りキャッシュを操作するクラスです。WEBページのキャッシュはアプリケーションごとに作られていて、sharedURLCacheでデフォルトのキャッシュを取得する事ができます。このデフォルトのインスタンスから、例えばキャッシュをすべて削除したりすることができます。

[[NSURLCache sharedURLCache] removeAllCachedResponses];

ここで注意すべきなのは、NSURLCacheは、NSURLDownloadやNSURLConnectionに使われるものであり、最初に挙げた二つの簡易的なstringWithContentsOfURLやdataWithContentsOfURLは、NSURLCacheは使われていない点です。これらのメソッドは、一度ダウンロードしたページなら当然キャッシュから取ってきますが、NSURLCacheとは関係がないので、当然キャッシュをコントロールすることができません。

そういうわけで、NSURLCacheはNSURLDonwloadやNSURLConnectionのためのものという話でした。今回の話は、下記のドキュメントに詳しく記されています。

URL Loading System

stringByReplacingPercentEscapesUsingEncoding

上のタイトルはNSStringのあるメソッド名です。随分長いですね。これを使えば、下に記したような、よく検索エンジンなどで見かけるURLエンコード文字列を通常の文字列へデコードすることができます。

%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%89

適切なエンコーディングを指定してstringByReplacingPercentEscapesUsingEncoding:を使います。

[aString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

上の文字列は、UTF8でエンコードされた「エンコード」という文字列なので、これでデコードすることができます。

余談ですが、ニコニコ動画のapi/getflv?v=*****で取得できる動画情報も、これと同じURLエンコードされた文字列になっています。

thread_id=1199694376&l=301&url=http%3A%2F%2Fsmile-clb33.nicovideo.jp%2Fsmile%3Fv%3D1973912.7024&link=http%3A%2F%2Fwww.smilevideo.jp%2Fview%2F1973912%2F146678&ms=http%3A%2F%2Fmsg.nicovideo.jp%2F7%2Fapi%2F&user_id=14****&is_premium=0&nickname=***********&time=1199705918&done=true

これもUTF8でデコードすることができます。

thread_id=1199694376&l=301&url=http://smile-clb33.nicovideo.jp/smile?v=1973912.7024&link=http://www.smilevideo.jp/view/1973912/146678&ms=http://msg.nicovideo.jp/7/api/&user_id=14****&is_premium=0&nickname=***********&time=1199705918&done=true

字幕の重ね方

試しに作成したニコニコ動画のオフラインプレーヤーは、あれ以降暇もないので一向に手を付けていませんが、こちらも3月以降から開発を再開しようかと思っています。ただ、こちらは優先度が低いので期待はしないでください。

ところで、ニコニコ動画RC2のNICO NICO PLAYERは、できるだけ字幕が重ならないように、画面に表示されるようになっています。しかし、いくつかのコメントでは、このようにコメントが意図的に重ねられている場合があります。

ニコニコ動画(RC2)-【初音ミク】 ハジメテノオト 【3D PV】
NicoNicoPlayer

私も最初はどうやっているかと思いましたが、コメントを見ると謎が解けます。

<chat anonymity="1" date="1198973215" mail="green shita 184" no="33399" thread="1197148291" user_id="enTe42KmyVpdjxOg0z1vZR8TZ40" vpos="5120">ワタシは言葉って 言えない</chat>
<chat anonymity="1" date="1199159461" mail="ue big 184" no="33766" thread="1197148291" user_id="2HKMh5EoSwCCbO7GJjvlYzZ_Jt4" vpos="5043">

(たくさんの改行)

ワタシは言葉って 言えない</chat>

見てわかる通り、通常のshitaの字幕に加え、ueの字幕に改行をたくさん入れて位置を調整しているようです。

プレーヤーを作成する時は、いわゆる職人技のコメントが正しく表示されるように微調整が必要なので、Bindingでリアルタイムに補正できるように作れたらいいなと思っているところです。

あけましておめでとうございます

昨年と全く同じタイトルですね、もうこれしかないです。早々に今年の目標を決めたいと思っていますが、その前に、昨日振り返るのを忘れていた昨年の目標の話です。

昨年の目標は、「表現しながら勉強する」だったようです。「だったようです」というのは、昨年の目標がいつの間にか「人に教えながら勉強する」だと思っていたからです。詳しくは昨年2日のブログをご覧ください。

目標の達成度といいましょうか、5割くらいだったでしょうか。本当はブログとかで積極的にOutputしていたので6割くらいでもいい気がしますが、目標を途中で忘れるという愚かな行為をおかしてしまったので5割ということで。

さて、昨年の話はもうこれまでにして、今年の目標と言いたいところですが、実はまだ全然決まっていません。3日までの休み中までにゆっくりと決めたいと思います。

関係ない話ですが、久しぶりにPathクラスを見たら異様に書き直したい衝動に駆られて、ついコーディングに走ってしまいました。。こんなことしてる場合でもないんだけどなぁー。