AS3のメモリ管理
ActionScript3にはGCがあるため、基本的にNewしたオブジェクトに対してDeleteは必要ありません。ガベージコレクタはどこからも参照がなくなったオブジェクトを勝手にメモリから開放してくれます。
ここで勘違いされがちな問題として、removeChildしたオブジェクトは画面から消えるために参照から外れたもの思っている人が多いようです。
addChildしたオブジェクトをremoveChildしただけではメモリから開放されないため、使い終わったオブジェクトにはnullを入れ参照を切らなければいけません。
また、子オブジェクト内で使用したオブジェクトも開放をしないと、親クラスからnullを入れても開放されません。また、リスナーに関しても子オブジェクト内でaddEventListnerをしたまま親からnullを入れても開放されることはありません。必ずremoveEventListenerをして開放をしましょう。マウス関係のリスナー等は開放忘れが頻発するので注意が必要です。
下記テストプログラムでは画面をクリックすることでオブジェクトの確保と開放を繰り返します。
memTest.as
package { import flash.display.Sprite; import flash.events.MouseEvent; public class memTest extends Sprite { private var o:CObj = null; public function memTest() { stage.addEventListener(MouseEvent.CLICK,onClickHandler); } private function onClickHandler(evt:MouseEvent):void{ if(o==null){ o=new CObj(); o.x=100; o.y=100; stage.addChild(o); }else{ stage.removeChild(o); o=null; } } } }
CObj.as
package { import flash.display.Sprite; import flash.events.Event; public class CObj extends Sprite { public function CObj() { super(); this.addEventListener(Event.ENTER_FRAME,onEnterframeHandler); this.addEventListener(Event.REMOVED,onRemovedHandler); this.graphics.beginFill(0); this.graphics.drawRect(0,0,100,10); this.graphics.endFill(); } private function onRemovedHandler(evt:Event):void{ if(evt.target!=this)return; this.removeEventListener(Event.REMOVED,onRemovedHandler); this.removeEventListener(Event.ENTER_FRAME,onEnterframeHandler); } private function onEnterframeHandler(evt:Event):void{ this.rotation++; } } }
CObj.as側でのポイントはEvent.REMOVEDで呼び出されるonRemoveHandlerです。
ご存知のとおり、AS3にはデストラクタが存在しないため、代わりにdestroy等のファンクションを作って呼び出す方法が取られますが、開放の後にdestroy関数を呼び忘れる可能性があります。
そこで、今回の方法を使用します。Event.REMOVEDはremoveChildがターゲットに対して実行された際に呼び出されます。そのためこれを監視することで、事実上デストラクタの代わりに使用することが可能となります。
注意点として、onRemoveHandlerは自分の保持する子オブジェクトをremoveChildしても呼び出されてしまうため、if(evt.target!=this)return;で、開放されたターゲットが自分自身でない場合は実行しないようにする必要があります。
この手法で全てのクラスがremoveHandlerをもつことで、必要なくなったオブジェクトはremoveChildしてあげれば連鎖的に末端のオブジェクトまでメモリが開放されることとなります。