Archive for December, 2009

何时不使用弱引用?

Friday, December 25th, 2009

根据David Coletta的文章Do you need a weak reference in your event listener?,在用匿名方法闭包作为事件侦听器的时候,需要使用强引用,不能使用弱引用。如果使用弱引用的话,则匿名方法对象没有任何引用,它将会被当作垃圾回收掉,侦听器也就无法响应事件了。

何时使用弱引用

Thursday, December 24th, 2009

坛子里经常有同志问道“何时使用弱引用”这个问题。虽然很想三下五除二帮大家解决它,可惜自身功力不够,只好求助于无所不知的谷歌。更换若干关键词后,最终找到了Dan Schultz发布的一篇文章When to Use Weak References。标题和问题一致的不像话,因此也就不需要额外的加工了,直接翻译给英文有困难的同志。虽然很不想说,但是还是要强调一下,不管之前大家鸟语学的怎么样,如果真的对编程感兴趣(对用它来吃饭感兴趣也成),希望有机会钻进去,了解更多、更新、更牛的东西,花点时间在鸟语上肯定会收获不小的。


  1. 给一个单例或任何一个预期将贯穿应用程序一生的实例添加事件侦听器的时,总是使用弱引用。记住,这条规则应用于大多数Flex的 manager 类,包括SystemManager,以及用单例模式实现的任何事件派发类。
  2. 添加Cairngorm Command时,总是使用弱引用。Cairngorm中的事件派发(CairngormEventDispatcher)是单例,它会一直保持这些Command的引用。
  3. 据Ted说,弱引用比强引用慢10倍。(涉及到访问成员变量时是这样的;事件侦听时不是。)
  4. 给实例自身添加事件侦听器时,使用强引用是安全的。(例如,派发事件的实例)
  5. 给Timer类的实例添加事件侦听器的时候应该使用弱引用。
  6. 最后,当不再需要时,要确保移除任何使用中的侦听器。我按照.Net的做法设计了一个接口IDisposable。该接口仅包含了一个方法dispose(),我在这个方法里移除任何可能处于使用中的事件侦听器。还有,除此之外,建议清除任何可能处于使用中的BitmapData对象。大多数,虽然不是全部,我写的类都实现了这个接口,当我晓得某个对象不再需要的时候,只需要确保调用了dispose()就可以了。

虽然作者给大家提出了这么多建议,按照笔者的经验,如果实在不确定是否该使用弱引用,那么就干脆不考虑了,都用强引用,在哪个文件里添加了侦听,就在哪里文件里把它移除。弱引用,强引用,不管怎么样,最终不需要的时候都是要被移除的。(如果不移除,一是可能会造成内存泄漏,使用强引用的时候;二是,因为AVM的GC是不确定的,所以侦听器仍然会响应事件,当你确定一个实例已经被移除,但是它却响应了你派发的事件的时候,灾难便降临了,使用弱引用的时候。这种情况无法确定其是否存在。在Mozilla的MMgc中有这样一段话:

  1. A GCObject is allocated with parameterized operator new, ……
    class MyObject : public MMgc::GCObject { ... };
    MyObject* myObject = new (gc) MyObject();
    
  2. The GetWeakRef method returns a weak reference to the object. Normally, a pointer to the object is considered a hard reference — any such reference will prevent the object from being destroyed. Sometimes, it is desirable to hold a pointer to a GCObject, but to let the object be destroyed if there are no other references. GCWeakRef can be used for this purpose. It has a get method which returns the pointer to the original object, or NULL if that object has already been destroyed.

    GCWeakRef *GetWeakRef() const;

按笔者的理解,在下面的例子中,weakReference就是一个GCObject,addEventListener的时候会给weakReference添加一个弱引用。当weakReference对象除了刚才添加的弱引用,还有其他引用的时候,GetWeakRef会返回一个GCWeakRef对象,该对象的get方法可以返回刚才添加的弱引用,此时weakReference不会被回收;如果只有刚才添加的弱引用了,那么GetWeakRef会返回一个null对象,因此GCObject没有任何引用,可以被GC回收了。


下面是我对弱引用和强引用的一个小实验:

1.有两个类WeakReference和StrongReference。

2.在主应用程序中有上述两个类的变量声明weakReference和strongReference。这两个变量没有引用任何实例。

3.在setReference方法里初始化两个类的实例,并引用这些实例。用弱引用添加事件侦听到weakReference的listen方法,用强引用添加事件侦听到strongReference的listen方法。这时,两个变量分别引用了两个类的实例。

strongReference:[object StrongReferenceListener]
weakReference:[object WeakReferenceListener]

4.在clearReference中清除变量weakReference和strongReference对实例的引用。

strongReference:null
weakReference:null

5.派发刚才所侦听的事件。

因为strongReference的事件侦听使用的是强引用,所以,虽然我们清除了对StrongReference实例的引用,但是其仍然驻留在内存中,因此仍然可以侦听到事件。此时造成了内存泄漏。

6.如果我们不移除对WeakRefence实例的引用,那么该实例始终会侦听事件。

结论:

1.对于强引用,一个实例需要被移除并回收时,如果该实例侦听了任何来自外部的事件,则需要移除该实例对该事件的侦听。如果事件来自自身,或者来自某个子级的对象,则可以不移除对该事件的侦听。

2.对于弱引用,当实例被移除时,下次垃圾回收时亦会回收该实例所侦听的事件。因此,只要该实例存在于内存中没有被回收,我们就可以认为事件侦听是有效的。相当于事件侦听与实例绑定在了一起,同生共死。

public class MyListeningClass implements IEventDispatcher
{

public function MyListeningClass()
{
    addEventListener("selfEventType",selfListener);
    childDispatcher.addEventListener("childEventType",childListener);
} 

private var childDispatcher:IEventDispatcher;

//You do not need to remove me by hand.
private function selfListener(event:Event):void
{
} 

//You do not need to remove me by hand.
private function childListener(event:Event):void
{
} 

public function addListeners(foreignDispatcher:IEventDispatcher):void
{
    dispatcher.addEventListener("strongListenedEvent",strongListener);
    dispatcher.addEventListener("weakListenedEvent",weakListener,falst,0,true);
}

//I am a strong referenced listener, you must remove me before you
//clear up the current instance.
public function strongListener(event:Event):void
{

}
//you do not need to remove me by hand. I will be
//removed automatically as the current instance cleared up.
public function weakListener(event:Event):void
{

}
}

有一个问题,当移除了弱引用事件侦听器所在的实例后,该事件侦听会立刻被回收吗?是否仍然驻留于内存,直到某个“时刻”,被垃圾回收器回收?如果是这样,实例被移除之后,该时刻之前,岂不是一段比较危险的时期?不过,从上述实验来看,一旦移除了实例,实例的事件侦听器亦会被同时移除。当然,也许没有被移除,总之,它不会响应事件了。

盛唐夜歌

Thursday, December 24th, 2009

奉天承运,皇帝昭曰。龙膏酒我醉一醉吧,葡萄美酒夜光杯,颁赐群臣品其味,金鼎烹羊记得添肉桂。胡姬酒肆灯花泪,以黄金销尽一宿魅,雾雨轻挠美人背,赏丝竹罗衣舞纷飞。

鱼玄机还不速为朕献舞一曲。

长安柳絮飞,箜篌响,路人醉,花坊湖上游,饮一杯来还一杯。水绣齐針美,平金法,画山水,诗人笔言飞,胭脂扫蛾眉。烟花随流水,入夜寒,寒者醉,今朝花灯会,提画灯迷猜一对。

阳羡茶浮水,琵琶绕,玉笛回,丁祭佾舞备,铜镜云鬓美。

脚腕间璎珞如翡翠,飞天绘。院落中百花还挂着露水。客栈里将军已征战回,战马还未睡着佳人盼着月归。

盛唐城门内,智者狂,痴者悲,愚者酒一壶,依柳早就入睡。

裴旻将军舞剑器划惊堂一虹动天地,豪卷添墨长安曲将狂草一笔指张旭。

再后来,古人又云,昨夜星辰昨夜风,画楼西畔桂堂东。身无彩凤双飞翼,心有灵犀一点通。

长安柳絮飞,箜篌响,路人醉,花坊湖上游,饮一杯来还一杯。水绣齐針美,平金法,画山水,诗人笔言飞,胭脂扫蛾眉。烟花随流水,入夜寒,寒者醉,今朝花灯会,提画灯迷猜一对。

阳羡茶浮水,琵琶绕,玉笛回,丁祭佾舞备,铜镜云鬓美。

脚腕间璎珞如翡翠,飞天绘。院落中百花还挂着露水。客栈里将军已征战回,战马还未睡着佳人盼着月归。

瓦如翚斯飞,掉琉璃,迎风吹,盛唐扬长帆,一句诗还一场醉,皇梁盘龙背,上银鳞,气势辉。银月飞天舞,空留西厢我不回。


不会写词作曲,只好跟着EDIQ感受一下盛唐的豪情万丈与柔情似水了。
痛一痛,笑一笑,让时间摆平一切,哈哈。

Motto

Thursday, December 24th, 2009

1. This moment will nap, you will have a dream; but this moment study, you will interpret a dream.

2. I leave uncultivated today, was precisely yesterday perishes tomorrow which person of the body implored.

3. Thought is already is late, exactly is the earliest time.

4. Not matter of the today will drag tomorrow.

5. Time the study pain is temporary, has not learned the pain is life-long.

6. Studies this matter, lacks the time, but is lacks diligently.

7. Perhaps happiness does not arrange the position, but succeeds must arrange the position.

8. The study certainly is not the life complete. But, since continually life part of - studies also are unable to conquer, what but also can make?

9. Please enjoy the pain which is unable to avoid.

10. Only has compared to the others early, diligently, can feel the successful taste.

11. Nobody can casually succeed; it comes from the thorough self-control and the will.

12. The time is passing.

13. Now drips the saliva, will become tomorrow the tear.

14. The dog equally study, the gentleman equally plays.

15. Today does not walk, will have to run tomorrow.

16. The investment future person will be, will be loyal to the reality person.

17. The education level represents the income.

18. One day, has not been able again to come.

19. Even if the present, the match does not stop changes the page.

20. Has not been difficult, then does not have attains.

做学问的三层境界——王国维

Wednesday, December 23rd, 2009

昨夜西风凋碧树。独上高楼,望尽天涯路。


衣带渐宽终不悔,为伊消得人憔悴。


众里寻他千百度,蓦然回首,那人却在灯火阑珊处。