常见的事件和委托,都包括两部分:对象和方法,当然如果委托到静态方法上,对象是为空的。
如果把事件委托到某个对象的方法上,同时就间接的引用了这个对象,导致其一直无法被回收,从而造成内存泄漏。
弱引用Action,原理就是把委托拆分,然后弱引用对象部分,需要调用委托的时候,再把对象“拉”回来,如果对象被回收了,就没有必要再调用它的方法了。
Nuget包:NewLife.Core
源码:https://github.com/NewLifeX/X/blob/master/NewLife.Core/Event/WeakAction.cs
视频:https://www.bilibili.com/video/BV1pg411h7So
基本用法
WeakAction设计核心在于,从函数委托中把对象和方法拆分。因此有基础的构造函数
public WeakAction(Object target, MethodInfo method); public WeakAction(Object target, MethodInfo method, Action<Action<TArgs>> unHandler, Boolean once);
直接传入目标对象和方法,很容易理解,但是很生硬,用起来也诸多不便。还需要有个传入委托的构造函数
public WeakAction(Delegate handler); public WeakAction(Delegate handler, Action<Action<TArgs>> unHandler, Boolean once);
Delegate参数意味着可以传入任意委托。
UnHandler用于在对象已经被回收时,额外执行的一个动作,一般用来解除事件绑定。
Once 表示该弱引用委托只能使用一次,然后就会回收。
核心原理
WeakAction在执行Invoke时,首先通过弱引用取得对象,如果对象已经被GC回收,此时将会取得空值。否则,执行委托方法触发被绑定的事件。
/// <summary>调用委托</summary> /// <param name="e"></param> public void Invoke(TArgs e) { //if (!Target.IsAlive) return; // Keep in mind that,不要用上面的写法,因为判断可能通过,但是接着就被GC回收了,如果判断Target,则会增加引用 Object target = null; if (Target == null) { if (Method.IsStatic) Reflect.Invoke(null, Method, e); } else { target = Target.Target; if (target != null) { // 优先使用委托 if (Method is MethodInfo mi) mi.As<Action<TArgs>>(target).Invoke(e); else target.Invoke(Method, e); } } // 调用方已被回收,或者该事件只使用一次,则取消注册 if ((Target != null && target == null || Once) && UnHandler != null) { UnHandler(Handler); UnHandler = null; } }
总结
WeakAction的核心功用是规避事件型内存泄漏,让事件方法所在对象不受牵挂的能够被GC回收。