前回はリファレンスカウンタの仕組みと目的について説明しました。それにしても、リファレンスカウンタと言うのは誤解を招きかねない名前ですね。確かに、仕組み的には参照(リファレンス)なのかもしれませんが、目的は保持なのですから、保持カウンタとすべきだと私は思います。実際、入門書によっては保持カウンタと書いてあるものもあるくらいです。ここでの説明もどうしようかと迷ったのですが、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];
この様にautorelease poolを使えば、releaseを予約しておいて、後でそのメッセージを送ってもらうことができます。
それでは、最初に説明した例で、autoreleaseを使ってみましょう。まず、オブジェクトaでは、最初にautorelease poolを生成しておきます。次にオブジェクトbにオブジェクトcを生成して返してもらうように依頼をかけます。オブジェクトbは、オブジェクトcを生成した後に、autoreleaseメッセージを送って、将来自動的に解放されるように予約してからアドレスを返します。オブジェクトbにとっては、オブジェクトcはもう解放してしまったも同然ですが、オブジェクトaはオブジェクトcを使うことができます。そして、オブジェクトaがautorelease poolを解放した時点で、オブジェクトcにreleaseが送られ、オブジェクトcは解放されます(fig.02)。このようにして、自分で保持したものは自分で解放するという規約を守ることができました。
[fig.02]autorelease poolを使った例 |
ところで、こんな時にはいつもautorelease poolの生成と解放で前後を挟まないといけないのでしょうか。それではかえって面倒です。
結論から言いますと、CocoaでGUIを組んでいる時は、特別な場合を除き、自分でautorelease poolを生成する必要はありません。今日は長くなりましたので、この説明は次回に続きます。