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

前回はリファレンスカウンタの仕組みと目的について説明しました。それにしても、リファレンスカウンタと言うのは誤解を招きかねない名前ですね。確かに、仕組み的には参照(リファレンス)なのかもしれませんが、目的は保持なのですから、保持カウンタとすべきだと私は思います。実際、入門書によっては保持カウンタと書いてあるものもあるくらいです。ここでの説明もどうしようかと迷ったのですが、Objective-C Framework Referenceでは、これに関するメソッドの分類が、Managing reference countsとなっており、説明の中でもreference countとなっているので、それを尊重しました。

自分で解放できない場合

さて、前回、この仕組みだけでは足りないと書きました。リファレンスカウンタの肝は、必要であれば自分で保持し、自分で保持したものは自分で解放することです。この規約があるからこそ、自分のことだけを考えれば良くなり、プログラムは単純になるのです。でも、これが不可能なことがあります。それはどんな場合でしょうか。

例えば、オブジェクトaがオブジェクトbに依頼して、オブジェクトcを生成してもらい、そのアドレスを返してもらったとします。このオブジェクトcは、自分で生成したものではありませんから、オブジェクトaには解放する義務はありません。では、誰がいつ解放するのでしょうか?

オブジェクトcはオブジェクトbが生成したものですから、オブジェクトbが解放しなければなりません。それはいつかと言えば、オブジェクトbにとって必要なくなった時、つまりオブジェクトaにアドレスを返した後です。ところが、オブジェクトaにアドレスを返すということは、とりもなおさず制御がオブジェクトbからオブジェクトaに戻ることに他なりません。つまり、オブジェクトbのメソッドは処理が終わってしまっていますから、その後解放なんて出来ないのです。だからと言って、先に解放してからアドレスを返すわけにはいきません。解放した時点で、そのオブジェクトは無くなってしまうのですから(fig.01)。

依頼を受けてオブジェクトを生成し、そのアドレスを返す場合
[fig.01] 依頼を受けてオブジェクトを生成し、そのアドレスを返す場合

これを解決するためには、予約だけしておいて、後で解放してもらう仕組みが必要になります。これがautorelease pool です。

自分で解放できない場合

autorelease poolというのは、後で解放して欲しいオブジェクトを記録しておけるオブジェクトです。解放して欲しいオブジェクトを記録しておけば、このautorelease pool自身を解放した時点で、記録してあるオブジェクトに対してreleaseメッセージを送ってくれます。では、実際に使い方を見て行きましょう。次のプログラムを見て下さい。

 id pool = [ [ NSAutoreleasePool alloc] init];
 id c = [ [ C alloc] init];
 [ c autorelease];
 [ pool release];
  1. 1行目でautorelease poolを生成しています。autorelease poolも他のオブジェクトといっしょです。allocで生成し、initで初期化します。
  2. 2行目ではCというクラスのインスタンスオブジェクトを生成しています。
  3. そして3行目で、これをautorelease poolに登録しています。ここで、cをautorelease poolに登録するという行為を、c自身にautoreleaseというメッセージを送ることにより行っている点に注目して下さい。こうすると、自動的に一番最近生成されたautorelease poolにレシーバーが登録されますので、プログラムを組む上では、autorelease poolがどこにあるのかを知っている必要がありません。つまり、autoreleaseはreleaseと同じ感覚で使うことができます。そして、releaseメッセージを送った時と違って、この時点ではcは解放されません。
  4. その次の4行目で、poolにreleaseメッセージを送ってpoolを解放しています。poolは自分が解放される時、登録されているオブジェクト全てにreleaseメッセージを送ります。つまり、この時点でcにreleaseメッセージが送られ、cが解放されるわけです。

この様にautorelease poolを使えば、releaseを予約しておいて、後でそのメッセージを送ってもらうことができます。

autoreleaseで解放を予約する

それでは、最初に説明した例で、autoreleaseを使ってみましょう。まず、オブジェクトaでは、最初にautorelease poolを生成しておきます。次にオブジェクトbにオブジェクトcを生成して返してもらうように依頼をかけます。オブジェクトbは、オブジェクトcを生成した後に、autoreleaseメッセージを送って、将来自動的に解放されるように予約してからアドレスを返します。オブジェクトbにとっては、オブジェクトcはもう解放してしまったも同然ですが、オブジェクトaはオブジェクトcを使うことができます。そして、オブジェクトaがautorelease poolを解放した時点で、オブジェクトcにreleaseが送られ、オブジェクトcは解放されます(fig.02)。このようにして、自分で保持したものは自分で解放するという規約を守ることができました。

autorelease poolを使った例
[fig.02]autorelease poolを使った例

ところで、こんな時にはいつもautorelease poolの生成と解放で前後を挟まないといけないのでしょうか。それではかえって面倒です。

結論から言いますと、CocoaでGUIを組んでいる時は、特別な場合を除き、自分でautorelease poolを生成する必要はありません。今日は長くなりましたので、この説明は次回に続きます。

前頁目次次頁