AS3.0/Flex中的弱引用

弱引用英文叫做weak reference,與之相反的是強引用(strong reference)。引用不是對象本身,而是類似於指向對象的一個指針。通常都說當至少還存在一個引用指向某個對象的時候,這個對象就不會被gc,這裏所說的引用就是強引用,而不是弱引用。反過來說,即使有N多弱引用指向某個對象,而沒有一個強引用指向該對象時,這個對象也會被gc,當gc發生後,所有的弱引用指向的對象就不存在了。這就是強引用與弱引用的本質區別。

AS3中常用到弱引用的地方有addEventListener方法和Dictionary類。
addEventListener方法的參數表爲addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false)。其中第5個參數表示是否使用弱引用。也就是說當useWeakReference=false時,EventDispatcher.addEventListener就會在EventDispatcher對象中添加一個對listener這個函數對象的強引用;而當useWeakReference=true時,EventDispatcher.addEventListener方法在EventDispatcher對象中添加的是對listener的弱引用,此時如果這個listener函數對象只是作爲EventDispatcher對象的徵聽器被引用時,那麼gc執行時就會回收這個listener函數,從而EventDispatcher對象的這個徵聽器就不存在了。用簡單的代碼來證明一下:

var sp:Sprite = new Sprite();
var eventHandler:Function = function(e:Event){ trace(“event fired”); };
//添加弱引用
sp.addEventListener(Event.ENTER_FRAME,eventHandler,false,0,true);
//取消eventHandler對函數的引用
eventHandler = null;
//強制進行gc,此函數只能在debug版的fp中才有效
System.gc();
/*
運行結果:因爲sp.addEventListener中添加的是對eventHandler函數的弱引用,當唯一指向函數的強引用eventHandler變成null以後,這個徵聽器函數就會被gc。不過gc不是立即發生,因此要強制進行gc後才能看到在輸出窗口中不會有event fired輸出。如果在sp.addEventListener使用強引用,那麼當eventHandler變成null以後,即使進行強制gc,還是會不斷輸出event fired。因爲徵聽器函數還作爲sp的一個徵聽器被引用,而且是強引用,因此就不會被gc。
*/
上面的例子留下來了一個問題:如果sp.addEventListerner添加的是強引用,當指向徵聽器函數的eventHandler設爲空後,這個徵聽器函數仍然不會被gc,那麼怎樣才能讓它被gc掉呢?答案很顯然,也就是當sp被gc掉以後,那麼這個徵聽器函數也會被gc掉,因爲sp指向的對象中保留了對徵聽器的唯一強引用。sp怎麼才能被gc掉?從AS2走過來的人往往都容易想到removeChild方法。但是AS3中的removeChild方法作用只是把一個DisplayObject對象從Display List中刪除,同時也刪除Display List中所存在的對這個DisplayObject對象的引用。但是如果這個DisplayObject對象還被其它變量引用時,它就不會被gc。所以說removeChild是必須的,但不足夠,還需要刪除Display List之外的所有引用才行!當然如果這個DisplayObject不在Display List中的話,就沒有必要removeChild。下面也用一個例子說明一下:

var sp1:Sprite = new Sprite();
var sp2:Sprite = new Sprite();
var eventHandler:Function = function(e:Event){trace(“event fired”);};
//sp1添加對eventHandler的強引用
sp1.addEventListener(Event.ENTER_FRAME,eventHandler);
//sp2添加對eventHandler的弱引用,用於查看sp1被gc後徵聽器是否也被gc
sp2.addEventListener(Event.ENTER_FRAME,eventHandler,false,0,true);
//取消eventHandler對函數的引用
eventHandler = null;
//取消sp1對Sprite對象的引用使其可以被gc,因爲Sprite對象沒有添加到Display List中,所以不需要removeChild。
sp1 = null;
//強制進行gc,此函數只能在debug版的fp中使用
System.gc();
/*
運行結果:當取消eventHandler對函數的引用後,對函數唯一的強引用就只存在與sp1所引用的Sprite對象中,當sp1不再引用Sprite對象時,這個Sprite對象就會被gc,因此對徵聽器函數的唯一強引用也沒了,剩下sp2中對函數的引用是弱引用,因此強制gc後,sp2的徵聽器就沒了,所以就不會看到輸出event fired。同樣的如果取消sp1對Sprite對象的引用,而是使用sp1的removeEventListerner方法取消對徵聽器函數的引用,徵聽器函數也會被gc,而sp1引用的Sprite對象還存在。
*/
Dictionary的弱引用與addEventListener的原理相同,只不過Dictionary的弱引用指的是對key的弱引用,而不是對value的弱引用。實際上Dictionary對象中保留的是對value的強引用,而且就算value對應的key對象已經被gc了,Dictionary對象中對value的強引用還存在,而且還造成一個不好的影響,就是key被gc後,就無法刪除Dictionary對象中對value的強引用。因此我覺得在使用Dictionary對象的時候,如果要刪除key的話,應首先delete Dictionary對象中對value的強引用。
//true表示使用弱引用
var dic:Dictionary = new Dictionary(true);
//建立key和value,之所以把key和value都定爲函數,是爲了檢查其是否被gc掉
var key:Function = function (e:Event){trace(“key is alive”);};
var value:Function = function (e:Event){trace(“value is alive”);};
//把key和value都加爲弱引用的徵聽器,不影響它們被gc
addEventListener(Event.ENTER_FRAME,key,false,0,true);
addEventListener(Event.ENTER_FRAME,value,false,0,true);
//取消value引用,取消後函數唯一的強引用就存在dic中
value = null;
/*取消key引用,dic使用弱引用,因此函數會被gc。再此如果不刪除dic中對value的引用,以後就沒機會刪除了。刪除對value的引用可使用delete dic[key];*/
key = null;
//強制gc
System.gc();
/*
運行結果:當取消key對函數的引用後,函數就會被gc。而取消value引用後,在dic中還存在對函數的強引用,因此key對應的徵聽器不輸出了,而value對應的徵聽器還在輸出。而且在取消key引用之前沒有刪除dic對value的強引用,那麼之後只能通過gc掉dic才能使value對應的函數被gc。
*/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章