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

モサ伝が予定通り配信されていれば、今頃はWWDCの真っ最中だと思います。基調講演では驚くべき発表はあったのでしょうか(この原稿は、当然のことながら、それよりもずっと前に書かれています。)。

GUIにおけるautorelease

前回、autorelease poolの仕組みを説明しました。このautorelease poolですが、実を言えばCocoaでGUIを組んでいる時は、特別な理由が無い限り生成する必要がありません。何故なら、フレームワークが自動的に生成しているからです。それがどこで生成されているかと言うと、イベントループの中です。GUIのプログラムでは、マウスやキー入力等、様々なイベントを取り出し、これに対する処理を行うということを繰り返しますが、この一連の動作の繰り返しをイベントループと言います(今までMacでプログラムを組んで来た方にとっては、言うまでも無いことですね。)。Cocoaでは、このイベントループの中で、autorelease poolを生成、一回転すると解放、これを繰り返します。つまり、一回転する毎に、autoreleaseされたオブジェクトは解放されるわけです。

これを前回の例に当てはめますと、オブジェクトaが行っていたautorelease poolの生成と解放は必要無いことになります(fig.01)。

autoreleaseされたオブジェクトはイベントループに戻ると解放される
[fig.01] autoreleaseされたオブジェクトはイベントループに戻ると解放される

ここまで読んで勘の良い方は、それだったらreleaseなど使わずに、全てautoreleaseにしてしまっても動作上問題無いのでは無いか?と気付いたのでは無いでしょうか。はい。その通りです。イベントループに戻った時に解放されるのですから、全てautoreleaseにしてしまっても、理論上は問題ありません。実際、autoreleaseを使えば、どこで解放すれば良いのか考える必要がなくなりますから、場合によってはその方がプログラムが単純になることもあるくらいです。ですが、autoreleaseの方がreleaseよりもコストが高いということは、覚えておいてください。autorelease poolにオブジェクトが登録され、それが解放される時にreleaseメッセージが送られるのですから、単にreleaseメッセージを送るだけよりも、当然余分な処理が走ることになります。それに、生成した一時的なオブジェクトは、たとえ必要が無くなっても、全てイベントループに戻るまで保持されます。数が少なければ良いのですが、これが莫大な量になりますと、消費メモリの観点からも無視出来なくなります。これは好みかもしれませんが、私は極力こまめにreleaseするように心掛けています。元々Mac OSでプログラムを組んで来た方でしたら、あの面倒なLockとUnlockを使いこなしていたのですから、retainとreleaseを適切に行うことなど、どうということは無いと思うのは私だけでしょうか。

autoreleaseされたオブジェクトを所有する方法

autoreleaseされているオブジェクトはイベントループに戻れば解放されます。でも、これを解放して欲しく無い時にはどうすれば良いのでしょうか。そういう場合は、retainしておけば大丈夫です。retainすれば、リファレンスカウンタが1つ上がりますから、autorelease poolが解放されることによってreleaseが送られて来ても、カウンターは0になりません(fig.02)。従いまして、イベントループに戻っても解放されないことになります。これは、「継続的に必要であれば、retainを送って明確に所有する。」という規約にも合致します。もちろん、所有した場合は、インスタンス変数に格納しておく等して、後で解放できるようにしておく必要があります。

autoreleaseされたオブジェクトはretainメッセージを送ると所有できる
[fig.02]autoreleaseされたオブジェクトはretainメッセージを送ると所有できる

そのオブジェクトはautoreleaseされているのか

ところで、この様にオブジェクトがautoreleaseされているかどうかを意識する必要があるのでしょうか。こんなことを意識するなんて面倒ですよね。結論から言えば、意識する必要はありません。と言うより、意識すべきでは無いのです。前回、例を上げて説明する時に、「例えばオブジェクトaがオブジェクトbに依頼して、オブジェクトcを生成してもらい、そのアドレスを返してもらったとします。」と書きましたが、これは説明を分かりやすくするために、わざと適切で無い表現をしました。本当は、「オブジェクトaはオブジェクトbにオブジェクトcを要求する。」だけで、オブジェクトbがどうやってオブジェクトcを返しているのかは、意識するべきでは無いのです。例えば、オブジェクトbはオブジェクトcを一時的に生成しているのではなく、継続的に所有しているのかもしれません。この場合はautoreleaseはされていないでしょう。ひょっとしたら、オブジェクトcは決して解放されることが無い、オブジェクト定数かもしれません。この場合もautoreleaseはされていません。

この様に、ざっと考えただけでも、オブジェクトbがオブジェクトcを返す処理は何通りも考えられます。しかし、そのいずれの場合でも、オブジェクトcを継続的に使用したければretainをする。必要なくなったらreleaseする。これは変わりません。つまり、オブジェクトbの中身の処理を、知る必要は無いのです。しかも、中身の処理を知らなくても良いということは、将来、オブジェクトbの中身の実装が変わったとしても、何の影響も無いことを意味します。「意識すべきでは無い。」と書いたのは、これが理由です。

今回でretain、release、autoreleaseの説明は終わるつもりだったのですが、予想外に内容が膨らんでしまい、終わりませんでした。と、言うわけで、さらに次回に続きます。

前頁目次次頁