我们正在开发一款带有Unity3D引擎的游戏(使用Mono作为用户代码 - 我们的代码是用C#编写的)。
场景是我们有一个暴露事件的类,大约有250个注册到该事件(游戏的每个关卡对象都将自己注册到该事件):
// Every level registers itself (around 250 levels)
ScoresDataHelper.OnScoresUpdate += HandleOnScoresUpdate;
在销毁此场景时,每个对象都会从事件中取消注册:
ScoresDataHelper.OnScoresUpdate -= HandleOnScoresUpdate;
使用内置的分析器时,我看到一个巨大的内存分配,深入挖掘表明它是由于代表未注册。
我怀疑这是因为Delegates是不可变的,当它们链接在一起时,会创建新实例吗?
这是来自Unity的分析器的截图:
在处理大量事件订阅时,有没有办法避免这些内存分配?
答案 0 :(得分:5)
当您在comments中确认要取消订阅所有活动订阅时,没有理由逐个取消订阅。
您可以将事件设置为null。这将取消订阅事件中的所有内容而不分配任何内存。
this.OnScoresUpdate = null;
需要注意的一点是,您无法在ScoresDataHelper
课程之外执行此操作。它必须在ScoresDataHelper
类内。
答案 1 :(得分:2)
一个简单的解决方案是不首先使用事件。
// T,S,U is whatever your function takes and returns
private List<Func<T,S,U>> Listeners = new List<Func<T,S,U>>();
public void OnScoresUpdate(Func<T,S,U> listener){
Listeners.Add(listener);
}
// when you want to fire the event
foreach(var listener in Listeners){
listener(param1, param2);
}
// when you want to unsubscribe the listeners:
Listeners = new List<Func<T,S,U>>();
如果要避免内存问题,也可以使用弱集合,一旦元素被垃圾回收,就会自动删除侦听器。
答案 2 :(得分:0)
如果要取消订阅特定事件,可以将集合更改为HashSet。然后,您将删除一个监听器,如:
private HashSet<Func<T,S,U>> listeners = new HashSet<Func<T,S,U>>();
public void RemoveListener(Func<T,S,U> listener){listeners.remove(listener);}
答案 3 :(得分:0)
添加此答案,因为这是目前在Google上排名第一的结果:
根据these tests,向委托添加函数会产生208字节的垃圾,向event
添加委托会导致104字节乘以添加的事件数。因此,添加到事件的第十个委托将创建1kb的垃圾!我想象一个类似的退订测试会有相同的结果。上面使用List
或HashSet
实现自定义事件类的两个答案对于我自己在Unity中解决此问题的版本来说是一种简单有效的解决方案。