サイトトップ

Director Flash 書籍 業務内容 プロフィール

Macromedia Flash非公式テクニカルノート

予め配置したインスタンスの存在しないフレームに移動しても参照が消えない

ID: FN1103001 Product: Flash CS5 and above Platform: All Version: 10.2 and above/ActionScript 3.0

問題
予めタイムラインにMovieClipインスタンスを置いたフレームから、インスタンスの存在しないフレームに移動したとき、インスタンスの参照は引続き残ってしまうことがあります。


原因
本ノートでは、考えられる原因をふたつ採上げます。

[1]ガベージコレクションはインスタンスをすぐには消さない
たとえば、メインタイムラインに3つのフレームをつくり、初めのふたつのフレームにMovieClipインスタンスを置き、3フレーム目は空にしておきます(図001上図)。そして、3フレーム目のフレームアクションで、再生ヘッドの移動を止めます(図001下図)。

図001■2フレームにインスタンスを置いて3フレーム目で止める
図001上図
図001下図

タイムラインに置いたMovieClipシンボルの第1フレームに、以下のスクリプト001を書きます(図002)。このフレームアクションは、インスタンスのDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)にテスト用のリスナー関数を登録し、インスタンスとStageオブジェクト、および親タイムラインの参照を[出力]します。

スクリプト001■MovieClipインスタンスのDisplayObject.enterFrameイベントにリスナーを登録
    // フレームアクション: MovieClipシンボル(インスタンmy_mc)
  1. addEventListener(Event.ENTER_FRAME, xTest);
  2. function xTest(eventObject:Event):void {
  3.   trace(eventObject.target, stage, parent);
  4. }

図002■MovieClipシンボルの第1フレームにスクリプトを書く
図002上図
図002下図

[ムービープレビュー]を試すと、MovieClipインスタンスの存在しないフレームに移動しても、リスナー関数が引続き呼出され、[出力]の表示を繰返します。ただし、タイムラインからインスタンスが消えているため、Stageオブジェクトと親タイムラインの参照はともにnullになっています(図003)。

図003■インスタンスが消えてもリスナー関数は引続き呼出される
図003上図
図003下図

これはタイムラインからインスタンスがなくなっても、「ガベージコレクション」という仕組みが働くまで、オブジェクトはメモリから除かれないためです(F-site「DisplayObjectインスタンスの削除とガベージコレクション」参照)。

[2][TLFテキスト]がガベージコレクションを妨げる
MovieClipシンボルの中に[TLFテキスト]があると、ガベージコレクションが働かなくなるようです。たとえば、前掲図002のMovieClipシンボルの中に[TLFテキスト]を置くと(図004)、MovieClipインスタンスのないフレームに移っても、インスタンスはメモリから消えません。

図004■MovieClipシンボルの中に[TLFテキスト]を置く
図004左図 図004右図

対処法
「原因」の[1]と[2]それぞれについての対処法を述べます。

[1]インスタンスのないフレームに移るときイベントリスナーは削除する
前述のとおり、タイムラインからインスタンスが消えても、ガベージコレクションは直ちに働くとはかぎりません。ですから、インスタンスのないフレームに移るときには、要らないイベントリスナーは削除すべきです。

インスタンスがタイムラインから消えるとき、つまり親インスタンスの表示リストから取除かれる際には、DisplayObject.removedイベント(定数Event.REMOVED)が呼出されます。したがって、前掲フレームアクション(スクリプト001)につぎのスクリプト002を加えると、インスタンスのないフレームに移ったときイベントリスナーが削除されます。

スクリプト002■インスタンスがタイムラインから存在しなくなるときイベントリスナーを削除する
    // フレームアクションに追加
  1. addEventListener(Event.REMOVED, xCleanUp);   // タイムラインから消えるとき
  2. function xCleanUp(eventObject:Event):void {
  3.   removeEventListener(Event.ENTER_FRAME, xTest);
  4. }

[2]インスタンスのないフレームに移るとき[TLFテキスト]は削除する
MovieClipシンボルの中に[TLFテキスト]があるかぎり、インスタンスに対してガベージコレクションは働かないようです。したがって、インスタンスのないフレームに移るときには、シンボルの中に置かれた[TLFテキスト]も削除しなければなりません。

ただし、DisplayObject.removedイベントを用いるときには注意が必要です。なぜなら、イベントオブジェクトが表示リストの親インスタンスに送られるからです。[TLFテキスト]もDisplayObjectクラスを継承しています。そのため、[TLFテキスト]の削除がDisplayObject.removedイベントを起こしてしまうのです。このときのイベントを切り分けるには、イベントオブジェクトのEvent.targetプロパティを確かめます。

前掲図004のようにMovieClipシンボルの中に[TLFテキスト](TLFTextField)のインスタンスmy_txtが置かれていたとすると、前掲フレームアクション(スクリプト001)はつぎのスクリプト003に書替えます。

スクリプト003■インスタンスがタイムラインから存在しなくなるとき[TLFテキスト]を削除する
    // フレームアクション: MovieClipシンボル(インスタンmy_mc)
  1. import fl.text.TLFTextField;
  2. var my_txt:TLFTextField;
  3. addEventListener(Event.ENTER_FRAME, xTest);
  4. function xTest(eventObject:Event):void {
  5.   trace(eventObject.target, stage, parent);
  6. }
  7. addEventListener(Event.REMOVED, xCleanUp);  
  8. function xCleanUp(eventObject:Event):void {
  9.   var myTarget:DisplayObject = DisplayObject(eventObject.target);
  10.   trace(myTarget);   // 確認用
  11.   if (myTarget == this) {
  12.     removeEventListener(Event.ENTER_FRAME, xTest);
  13.     removeChild(my_txt);
  14.     my_txt = null;
  15.   }
  16. }

スクリプト003のリスナー関数(xCleanUp)はタイムラインから除かれるインスタンス(Event.targetプロパティ)が自らであることを確かめたうえで、イベントリスナーと[TLFテキスト]インスタンスを削除しています(第8〜16行目)。また念のため、[TLFテキスト]インスタンスの参照(my_txt)にもnullを代入して破棄しました。

[ムービープレビュー]を確かめると、インスタンスの存在しないフレームに移ったとき[TLFテキスト]インスタンスは削除され、ガベージコレクションによりメモリから消されます[*1]。なお、スクリプト003第10行目でEvent.targetプロパティの参照をtrace()関数に渡していますので、つぎのようにMovieClipと[TLFテキスト]のインスタンスそれぞれのDisplayObject.removedイベントについてインスタンスが[出力]されます。

[object Pen_1] [object Stage] [object MainTimeline]
[object Pen_1] [object Stage] [object MainTimeline]
[object Pen_1]
[object TLFTextField]

[*1] 前掲スクリプト003をつぎのように書替えれば、[ムービープレビュー]でガベージコレクションが働くことを確かめられます。なお、System.gc()はガベージコレクションを強制的に呼出すデバッグ用のメソッドです(前出「DisplayObjectインスタンスの削除とガベージコレクション」注[*3]参照)。

  1. import fl.text.TLFTextField;
  2. var my_txt:TLFTextField;
  3. addEventListener(Event.ENTER_FRAME, xTest);
  4. function xTest(eventObject:Event):void {
  5.   trace(eventObject.target, stage, parent);
      System.gc();   // 追加
  6. }
  7. addEventListener(Event.REMOVED, xCleanUp);  
  8. function xCleanUp(eventObject:Event):void {
  9.   var myTarget:DisplayObject = DisplayObject(eventObject.target);
  10.   trace(myTarget);
  11.   if (myTarget == this) {
  12.     // removeEventListener(Event.ENTER_FRAME, xTest);
  13.     removeChild(my_txt);
  14.     my_txt = null;
  15.   }
  16. }

作成者: 野中文雄
更新日: 2011年7月15日 Flash Player 10.2に対応するよう「対処法」[2]を大幅に改訂。
作成日: 2011年3月25日


Copyright © 2001-2011 Fumio Nonaka.  All rights reserved.