跳到主要內容

ActionScript3.0 記憶體釋放問題

大家好,我是奶綠茶
一個好的Flash作品, 除了要有美美的設計與酷炫的動態外
奶小茶我覺的更重要的應該是效能。
我們就以一個EnterFrame事件來說明好了。
在ActionScript2.0時, MovieClip的EnterFrame事件, 當該MovieClip被移除時
其事件也會自動消失。
但在ActionScript3.0, 把該MoviecClip移除, EnterFrame還是會在。
所以當你載入越多的swf檔時, 沒有把前一個swf事件都移除的話, 效能就會越來越差。
那要如何移除事件呢?
//偵聽
clip.addEventListener(事件類型:String , 偵聽者函式:Function)
//取消偵聽
clip.removeEventListener(事件類型:String , 偵聽者函式:Function);
那什麼時候要取消偵聽?
通常在移除可視物件時, 就要把所有的事件都取消掉, 這樣才不會一直佔住記憶體。
可視物件有個Event.REMOVE_FROM_STAGE可以加以利用
當物件被從stage移除時, 會廣播該事件。
當偵聽者聽到時, 就把所有有註冊的事件通通取消掉。

被載入的Loaded.swf
在建構函式時就偵聽Event.ENTER_FRAME事件。
package  {  
import flash.display.Sprite;
import flash.events.Event; 
public class Loaded extends Sprite{  
public function Loaded() {
this.addEventListener(Event.ENTER_FRAME, onEventEnterFrame);
}  
private function onEventEnterFrame(e:Event):void {
trace(e);
}  
} 
}
載入的主要SWF
package  {  
import com.bit101.components.PushButton;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLRequest;
[SWF(width = "950", height = "500", frameRate = "41", backgroundColor = "#000000", pageTitle = "milkmidi.com")] 
public class Main extends Sprite{  
private var _ldr:Loader
public function Main() {
_ldr = new Loader();
_ldr.load(new URLRequest("Loaded.swf"));   
this.addChild(_ldr);
var _btn:PushButton = new PushButton(this, 0, 0, "unload", onClick);   
}  
private function onClick(e:MouseEvent):void{
_ldr.unload();
}  
} 
}
發怖成功後, 可以看到面版不斷的輸出文字,
按下按鈕 ,unload該swf, 還是看到面版不斷的輸出文字, 也就表示物件移除
不代表事件移除, 所以我們改寫一下Loaded.swf的程式碼。
package  {  
import flash.display.Sprite;
import flash.events.Event; 
public class Loaded extends Sprite{  
public function Loaded() {
this.addEventListener(Event.ADDED_TO_STAGE, onAdd2Stage);
//當可視物件被加入至場景後, 會發出的事件。
this.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);   
//當可視物件被移除後至場景, 會發出的事件。
}   
private function onRemovedFromStage(e:Event):void {
//物件被移除, 所有有偵聽的事件也要手動取消掉。   
this.removeEventListener(Event.ADDED_TO_STAGE, onAdd2Stage);
this.removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
this.removeEventListener(Event.ENTER_FRAME, onEventEnterFrame);
}  
private function onAdd2Stage(e:Event):void {
this.addEventListener(Event.ENTER_FRAME, onEventEnterFrame);
}
private function onEventEnterFrame(e:Event):void {
trace(e);
}  
} 
}
這樣當物件被除掉後, 也可以一起把事件都取消掉

補充一下:
在google找了些相關資訊, 發現一種不正規的Hack方法
但真的能將記憶體給釋放:
import flash.net.LocalConnection;
import flash.system.System;
try{
new LocalConnection().connect("foo");
new LocalConnection().connect("foo");
}catch(error : Error){
} 
//透過try catch,讓Player的garbage collection強製他回收不用的記憶體
System.gc();
//強制執行記憶體回收程序

留言

匿名表示…
感謝奶綠老師的教學,
弟最近正被這個問題困擾了好久,
看過老師的文章後總算是解決一大半了...

希望明年能順利去上老師的AS3課程 :)
寫道…
在網上看到強制回收的方式,
不知道是不是一樣的效果,
as3的回收機制觀念有點不太懂

語法如下:
try{
new LocalConnection().connect("foo");
new LocalConnection().connect("foo");
}catch(error : Error){
}
奶大解個謎吧...我只是照做,根本不知道它幹了什麼事。
milkmidi寫道…
經過不小心google一下
發現這樣的做法是一種不正規的Hack方法
但真的能清掉記憶體
試試看再補上一句
try{
new LocalConnection().connect("foo");
new LocalConnection().connect("foo");
}catch(error : Error){
}
System.gc();
這樣能清的更乾淨
寫道…
System.gc();
在flash10的help找不到他的說明,倒是flex3上的有它的help。
有高手說這在air上比較有用,不是很確定。
自己試用system.gc()....感覺差異沒有很大。
匿名表示…
請問奶綠大大 在做flash網站時,也是運用此原理來製作嗎?例如像之前「海尼根」較大型的flash網站開發時,還有沒有需要注意的事項?
匿名表示…
System.gc();
奶老师,这个在flash里没方法用,我google了一下,说只能在Debug的下 用player 9 才可以,给在flash里没方法测试,您是在什么环境下用 System.gc();
的啊?
milkmidi寫道…
因為我都是安裝Debug版的FlashPlayer
所以還滿常用到這個方法的
匿名表示…
http://tw.myblog.yahoo.com/slamgundam/article?mid=1092&prev=-1&next=1049

有人貼你的文
milkmidi寫道…
還真的是,整個貼過去
只是把我的名字拿掉了