« ぐーーーー | Home | 松本見聞録 »

Apr 062008

Loader.removeChild

何故エラーになるか?

Error: Error #2069: Loader クラスは、このメソッドを実装しません。
at Error$/throwError()
at flash.display::Loader/removeChild()

DisplayObjectContainerを継承してるんじゃないのか??
ちゃうのか?
俺がまちがっとんのか?
あーあーあー。
あほう。

追記:06-Apr-2008 20:55:08
以下の検証には誤りがありました。
正確な検証結果はapeirophobia: 外部swfの削除はLoader.unloadは使わない方が良いんじゃないかという件に誤りがあった件を参照してください。スイマセン。

ちと検証として100回ほど外部SWFを読み込み、その後unloadして削除するというループを実行し、メモリ推移を記録してみる。結果はこれ。
080406.jpg
一応GCは適度に作動しているのだけど、100回のループが終了(unloadで終了)したあとに元のメモリに復帰するという感じではなさそう。その後しばらく放置してみるもGCが作動することは無い。
(まぁ他に何か作動したらGCが作動するのかもしれないけど、ある程度のメモリはゴミ許容ということなのだろうが、じゃあそれが確実に破棄されるのはいつなんだ?というのが見えないのはかなり不安だ。
GC待機の状態なのか、それともGC対象になっていないのか・・・。
それがわからんのはなんとかならんのかな?

追記:08/04/06 03:34:44
もう少し検証してみる。
外部ファイルを画像を含めた193kbのSWFファイルに設定。
100回loadとunloadを繰り返し、インターバルを4秒あけてそれを100回繰り返す。
つまり10000回外部swfのloadとunloadを繰り返してみる。
080406_1.jpg
loadをしない状態では8565kb。
080406_2.jpg
3000回目:16343kb。GCは作動しているが、緩やかに右肩上がり。
080406_3.jpg
5200回目:21320kb。右肩上がり変わらず。
080406_4.jpg
7500回目:20144kb。横ばい状態になってきた。
080406_5.jpg
8800回目:23978kb。まぁ増えたり減ったり。
080406_6.jpg
9100回目:24871kb。まぁさほど変わらず。
080406_7.jpg
10000回目:25973kb。まぁちょっとあがった。

最終的なメモリ増加量は17408kb。これは外部ファイルのサイズの約90倍のサイズ。

1次式と仮定してそれぞれのタイミングの傾きを求めてみる。
3000 2.592666667
5200 2.452884615
7500 1.543866667
8800 1.751477273
9100 1.791868132
10000 1.7408
な感じ。まぁ前半の角度が強いのは予想してた感じ。
後半に行けば行くほど緩やかになる。その先にドンドンゆるくなるのか?はよく分からないけれど・・。
ただ1.75あたりで収束しているような感じ。

この検証でも分からないのはGCが消費メモリの絶対値に依存して発動しているのか?というところ・・。
なんつーか結論の出ない検証結果で申し訳ないけど、一応記録しておく。

続き:08/04/06 05:06:31
今度はSystem.gc()を発動させてみる。(ただしデバッガプレイヤーのみ作動)

080406_b1.jpg
開始:8585kb
080406_b2.jpg
2700回目:17801kb。予想よりメモリが消費されてる。
080406_b3.jpg
5400回目:16810kb。減った。でも通常のものとさほど差が無い。
080406_b4.jpg
7700回目:21033kb。通常と同じような・・・
080406_b5.jpg
10000回目終了:23282kb。

各タイミングでの傾きは
2700 3.413333333
5400 1.523148148
7700 1.616623377
8800 1.749204545
10000 1.4697
最後にgcが発動しているので10000はかなり下がっているものの、それ以外の部分はさほど通常のGCと変わらない気がする。GCの発動条件が分からない以上これは同じ効果という結論でも良いような気がする。で、なんでGC強制しているのに増加していくのか?っつー話だわ。なんか俺がミスってる可能性もある。

ということで、参考までに使ったスクリプトは以下のとおり。
親Flash

var bt1 = new bt();
addChild(bt1);
bt1.addEventListener(flash.events.MouseEvent.CLICK,loadStart);
var bt2 = new bt();
bt2.x = 50;
bt2.addEventListener(flash.events.MouseEvent.CLICK,unloadWorld);
addChild(bt2);
//
var _world:Loader;
var count:uint = 0;
var count2:uint = 0;
tx.text = String(count2);
//timer
var timer:Timer = new Timer(4000,1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE,loadStart);
function loadStart(e) {
	count=0;
	loadWorld();
}
function loadWorld(e=null) {
	var request:URLRequest = new URLRequest("dummy.swf");
	_world = new Loader();
	addChildAt(_world,0);
	var dispatcher = _world.contentLoaderInfo;
	dispatcher.addEventListener(ProgressEvent.PROGRESS, onProgress);
	dispatcher.addEventListener(flash.events.Event.COMPLETE, onComplete);
	_world.load(request);
}
function onComplete(e) {
	unloadWorld();
}
function onProgress(e) {
	//trace("progressHandler",e);
}
function unloadWorld(e=null) {
	var dispatcher = _world.contentLoaderInfo;
	dispatcher.removeEventListener(ProgressEvent.PROGRESS, onProgress);
	dispatcher.removeEventListener(flash.events.Event.COMPLETE, onComplete);
	_world.unload();
	_world = null;
	if (++count<100) {
		loadWorld();
	} else if (++count2<100) {
		tx.text = String(count2);
		timer.start();
		//System.gc();//gc発動の場合
	}else{
		//System.gc();//gc発動の場合
	}
}
子供SWFは
this.loaderInfo.addEventListener(Event.UNLOAD, remove,false,0,true);
function remove(evt:Event) {
	this.loaderInfo.removeEventListener(Event.UNLOAD, remove);
}
っつーような感じ。 なんか消し忘れがあるのか??

4 Comments

んー、contentを直接removeはできないな・・。ちなみに子供の外部SWFからもparent.removeChild(this)もエラーであります。
仕方が無いのでloader.unloadを使って一応子供の中で
this.loaderInfo.addEventListener(Event.UNLOAD, remove);とかにしてremoveのなかでthis.loaderInfo.removeEventListener(Event.UNLOAD, remove);とか・・。
GCが作動するまでしばらく時間がかかるので不安になるけど一応GC作動すればきれいになる・・・はず。(ただある程度溜まらないと作動しないですね・・。これ画面描画負荷と関係あるかと思ったけど、何も動いてなくても作動するのには容量+時間間隔みたいな制限があるっぽいな・・。

unloadはイマイチですが、removedFromStageを使ってみると結構ガシガシGCが効いてる感じがする。まだ感じの段階だけれども・・。
も少し検証必要。

よく考えると
_world.unload();

loader.contentsはremoveされているものの、loader自体は削除されていない・・・汗
ということで
loader.unload();
removeChild(loader);
loader = null;
を試してみることにします・・。
片手落ちでスイマセン。

上記の検証やっぱ
removeChild(loader);
が抜けているのがおかしい結果の原因でした。
きちんとした検証はこちら
apeirophobia: 外部swfの削除はLoader.unloadは使わない方が良いんじゃないかという件に誤りがあった件

Leave a comment

Search and Archives