Platform: Windows
Version: 8.0
問題
Flash 8で報告された問題です。Alert.show()メソッドをComboBox.changeイベントに設定したコールバック関数内から呼出すと、つぎのようなエラーを発生することがあります。
1 つのアクション内で256レベルの反復を超えました。
無限ループの可能性があります。
このムービーではこれ以上のアクションの実行は無効になります。
問題を再現する手順は、つぎのとおりです[*1]。
- [ライブラリ]にAlertとComboBoxインスタンスを格納します。
- ComboBoxインスタンスをメインタイムラインに配置し、インスタンス名をmyComboBoxとし、[list]パラメータにドロップダウンリストの項目をふたつ以上設定します(図001)。
- メインタイムラインのフレームアクションに、以下のスクリプト001を設定します。
- [ムービープレビュー]を行い、ComboBoxインスタンスの項目を変更すると、上記のような256レベルの再帰を超えた旨のエラーが表示されます。
図001■ComboBoxインスタンスの[パラメータ]設定と[ライブラリ]パネル
スクリプト001■ComboBoxインスタンスの項目を選択するとAlertインスタンスを表示
// タイムライン: _root
// フレームアクション
import mx.controls.Alert;
import mx.controls.ComboBox;
var oListener:Object = new Object();
oListener.change = function(eventObject:Object):Void {
trace(eventObject.target);
Alert.show("message"); // 256レベル超再帰エラー!
};
myComboBox.addEventListener("change", oListener);
|
上記スクリプト001のComboBox.changeイベントハンドラメソッドには、イベントの発生したインスタンス(イベントオブジェクトのtargetプロパティ)をtrace()するステートメントが挿入してありますので、ComboBox.changeイベントが何度も繰返し呼出されていることが確認できます。
[*1] タイムラインにインスタンスを配置せず、スクリプトだけでテストしたい場合には、[ライブラリ]にAlertとComboBoxコンポーネントを格納したうえで、つぎのスクリプト002をフレームアクションに設定します。
スクリプト002■ComboBoxインスタンスを動的に生成してその項目を選択したときAlertインスタンスを表示
// タイムライン: _root
// フレームアクション
import mx.controls.Alert;
import mx.controls.ComboBox;
var myComboBox:ComboBox =
this.createClassChildAtDepth(ComboBox, DepthManager.kTop);
myComboBox.dataProvider = ["item0", "item1", "item2"];
myComboBox.move(10, 10);
var oListener:Object = new Object();
oListener.change = function(eventObject:Object):Void {
trace(eventObject.target);
Alert.show("message"); // 256レベル超再帰エラー!
};
myComboBox.addEventListener("change", oListener);
|
|
原因
Flash 8のバグだと思われます。Flash MX 2004では、この現象は確認されていません。Flash 8からFlash Player 7書出しをしても、問題は発生します。したがって、Flash 8に実装されている、AlertまたはComboBoxを構成するクラスに、バグがあるものと推測されます(後述「対処法」注[*2]をご参照ください)。
対処法
対処法を、3つご紹介します[*2]。
[1]Alert.show()メソッドの呼出しを遅らせる
ComboBox.changeイベントハンドラメソッド中でAlert.show()メソッドを呼出すと、イベントの呼出しが再帰してしまうようです。したがって、若干のタイムラグを与えてから、Alert.show()メソッドを呼出します。
つぎのスクリプト003は、Flash 8で実装されたsetTimeout()関数を用いて、100ミリ秒後にAlert.show()メソッドを呼出しています[*3]。
スクリプト003■setTimeout()関数でAlert.show()メソッドの呼出しを遅らせる
// タイムライン: _root
// フレームアクション
import mx.controls.Alert;
import mx.controls.ComboBox;
var oListener:Object = new Object();
oListener.change = function(eventObject:Object):Void {
// Alert.show("message");
setTimeout(Alert.show, 100, "message");
};
myComboBox.addEventListener("change", oListener);
|
[2]Alert.show()メソッドを呼出す間リスナーオブジェクトを外す
Alert.show()メソッドを呼出したときにComboBox.changeイベントが再呼出しされてしまうようですので、呼出す前にリスナーオブジェクトをComboBoxインスタンスから一旦外し、呼出し後に再設定することで再帰が避けられます
スクリプト004は、Alert.show()メソッドを呼出す前に、ComboBoxインスタンスからEventDispatcher.removeEventListener()メソッドでリスナーオブジェクトを一旦除き、呼出し後にEventDispatcher.addEventListener()メソッドでオブジェクトを再度リスナー登録しています。
スクリプト004■Alert.show()メソッドを呼出す間リスナーオブジェクトを外す
// タイムライン: _root
// フレームアクション
import mx.controls.Alert;
import mx.controls.ComboBox;
var oListener:Object = new Object();
oListener.change = function(eventObject:Object):Void {
var _cb:ComboBox = eventObject.target;
_cb.removeEventListener("change", this);
Alert.show("message");
_cb.addEventListener("change", this);
};
myComboBox.addEventListener("change", oListener);
|
[3]Alert.show()メソッドを呼出す前にComboBoxインスタンスからフォーカスを外す
ComboBoxインスタンスからフォーカスを外すことによっても、問題は回避できるようです。Aletウィンドウを表示する際には、ComboBoxにフォーカスを保持する必要はありません。スクリプト005は、Alert.show()メソッドを呼出す前に、Selection.setFocus()メソッドでComboBoxインスタンスからフォカスを外しています。
スクリプト005■Alert.show()メソッドを呼出す前にフォーカスを外す
// タイムライン: _root
// フレームアクション
import mx.controls.Alert;
import mx.controls.ComboBox;
var oListener:Object = new Object();
oListener.change = function(eventObject:Object):Void {
Selection.setFocus(null);
Alert.show("message");
};
myComboBox.addEventListener("change", oListener);
|
[*2] Alertクラスを修正すると、一応問題は回避することができます。
静的メソッドAlert.show()のfunctionブロック内で、PopUpManager.createPopUp()メソッドに渡すオブジェクトから、validateNowプロパティを削除(コメントアウト)します(スクリプト006)。すると、ComboBox.changeイベントハンドラメソッド内からAlert.show()メッセージを呼出しても、エラーは発生しなくなります。
スクリプト006■Alertクラス
// Alertクラス
class mx.controls.Alert extends Window
{
// ...[中略]...
static function show(text,title,flags,parent,listener,icon,defButton):Alert
{
var o = new Object();
// ...[中略]...
o.validateNow = true; //←コメントアウトでエラー解消
var m = PopUpManager.createPopUp(parent,Alert, modal, o);
// ...[中略]...
m.addEventListener("click", listener);
return m;
}
|
もっともこの修正が、Alertクラスだけでなく、PopUpManagerその他のクラスとの連携に与える影響を確実に把握しないかぎり、クラスに直接手を加えることには慎重になるべきでしょう。
[*3] setInterval()関数やMovieClip.onEnterFrameイベントハンドラメソッドを使って、呼出しを遅らせても結構です。
|
_____
作成者: 野中文雄
協力者: youich
作成日: 2006年6月29日