【ActionScript】Event.REMOVED_FROM_STAGEは便利だけど注意が必要かも
Flash の ActionScript で、ボタンなどに addEventListener を使ってイベントを設定した後、イベントを removeEventListener を使って削除しないと、どんどんとメモリを消費していきます。
このイベントの削除し忘れがよく発生するので、私がよく利用しているのが、画面遷移などでそのMovieClip が消える時にトリガーされる Event.REMOVED_FROM_STAGE を利用する方法。
例えば
- サンプル1
btn.addEventListener(MouseEvent.CLICK,hogehoge); btn.addEventListener(Event.REMOVED_FROM_STAGE,removeEventHandler); ... function hogehoge(event:MouseEvent):void{ //ボタンイベントが入ります ... } function removeEventHandler(event:Event):void{ var mc:MovieClip = MovieClip(event.currentTarget); mc.removeEventListener(Event.REMOVED_FROM_STAGE,removeEvent); switch(mc.name){ case "btn": mc.removeEventListener(MouseEvent.CLICK,hogehoge); break; } }
Event.REMOVED_FROM_STAGE は stage 上からターゲットとなる MovieClip が除外される際にトリガーされます。
従いまして、タイムラインでステージ上にあらかじめ配置している MovieClip でも、addChild で追加された MovieClip でも、そのイベントが付加されたターゲットが消えるタイミングで、処理を実行することができるので、追加したイベントの削除忘れが防げるので便利。
さて、ここからが本題。
MovieClip が削除される時にトリガーがかかるということは、removeChild で削除しない限りは、画面遷移の時にトリガーがかかるということ。
すなわち、画面切り替え時に処理したい項目を、ここで記載しておけば良いのでは?
ということを考えて、上記の removeEventHandler の中に、遷移時の処理を併記してしまったのです。
例えば、他の MovieClip に追加したイベントを削除するとか、初期化するとか、そういうたぐいの物。
先ほどのコードに例として追加すると、以下のようなもの
- サンプル2
btn.addEventListener(MouseEvent.CLICK,hogehoge); btn.addEventListener(Event.REMOVED_FROM_STAGE,removeEventHandler); btn2.addEventListener(MouseEvent.CLICK,fugofugo); ... function hogehoge(event:MouseEvent):void{ //ボタンイベントが入ります ... } function fugofugo(event:MouseEvent):void{ //ボタンイベントが入ります ... } function removeEventHandler(event:Event):void{ var mc:MovieClip = MovieClip(event.currentTarget); mc.removeEventListener(Event.REMOVED_FROM_STAGE,removeEvent); switch(mc.name){ case "btn": mc.removeEventListener(MouseEvent.CLICK,hogehoge); break; } if(btn2){ if(btn2.hasEventListener(MouseEvent.CLICK)){ btn2.removeEventListener(MouseEvent.CLICK,fugofugo); } } }
「btn2」という MovieClip に追加した 「fugofugo」というイベントを、「btn」が消える時に合わせて削除するという意図です。
一見すると、サンプル2のコードで動作するようにも見えます
ところが、このコードでは、成功することもあれば、失敗することもあります。
その理由は、MovieClip が削除される順番によって、成功/失敗が変化するためです。
サンプル2の場合、Event.REMOVED_FROM_STAGE イベントを付加した「btn」という MovieClip が、「btn2」よりも先にステージから消える場合は、「btn」に付加している「removeEventHandler」が呼び出されるので、意図した通りの動作として、「btn」「btn2」のイベントを全て削除することができます。
しかし、「btn2」が先にステージから消え、続いて「btn」がステージから消える場合は、「btn2」が消える時には上記の「removeEventHandler」は呼び出されることがないため、「btn2」のイベントは残ったままとなります。
この部分を横着してしまったため、キッチリと想定通りの動作をしなくて、その原因を調べるのに少し時間がかかってしまいました。
上記のコードをちゃんと動作するものにするならば、
- サンプル3
btn.addEventListener(MouseEvent.CLICK,hogehoge); btn.addEventListener(Event.REMOVED_FROM_STAGE,removeEventHandler); btn2.addEventListener(MouseEvent.CLICK,fugofugo); btn2.addEventListener(Event.REMOVED_FROM_STAGE,removeEventHandler); ... function hogehoge(event:MouseEvent):void{ //ボタンイベントが入ります ... } function fugofugo(event:MouseEvent):void{ //ボタンイベントが入ります ... } function removeEventHandler(event:Event):void{ var mc:MovieClip = MovieClip(event.currentTarget); mc.removeEventListener(Event.REMOVED_FROM_STAGE,removeEvent); switch(mc.name){ case "btn": mc.removeEventListener(MouseEvent.CLICK,hogehoge); break; case "btn2": mc.removeEventListener(MouseEvent.CLICK,fugofugo); break; } }
とすれば、きっちと動作します。
サンプル2よりもサンプル3の方が、コード的には短いのに、なぜ私はサンプル2のようなコードを書いたのか?
それは、MovieClip に MouseEvent 以外の処理を加えていたからです。
上記のサンプルでは、分かりやすくするために、MovieClip「btn」「btn2」に MouseEvent を追加するコードにしましたが、実際には、「btn2」 の MouseEvent に該当する部分に、TimerEvent だったり、Event.ENTER_FRAME のイベントだったりを記載していました。
それらのイベントを追記する都度、 Event.REMOVED_FROM_STAGE を追記するのが面倒だったので、上記のように、MovieClip の1つに代表で追加して、処理をするようなコードを書いていました。
結論としては、横着してはダメだということですね。
イベントリスナーの追加、削除については、他にもっと良い書き方がないかと、常々思っていますので、思いついたり、発見したらブログにてまた改めて書きたいと思います。