Answers:
为什么事件侦听器不应该超过注册它们的对象?似乎您假设事件监听器应该由控件的方法注册(如果以GUI示例为例),或更准确地说,是由继承GUI工具包控件的类的对象注册的方法。这不是必须的-例如,您可以使用专门的对象注册事件侦听器,然后再丢弃该对象。
另外,如果事件侦听器被弱引用,则即使您从未使用过该引用,也必须实际上保留对其的引用。否则,将导致随机收集侦听器。因此,我们得到了一个错误
如果避免该错误的动机不足,那么可以采取以下措施:
您必须为创建的每个侦听器考虑一个名称。
某些语言使用静态分析,如果您有一个永远不会被写入或永远不会被读取的私有成员字段,它将生成警告。您必须使用一种机制来覆盖它。
事件侦听器会执行某些操作,一旦收集到具有强引用的对象,它就会停止执行此操作。现在,您有一些会影响程序状态并取决于GC的东西-这意味着GC会影响程序的具体状态。这是坏的!
处理弱引用的速度较慢,这是因为您具有另一个间接级别,并且需要检查引用是否已收集。如果有必要在弱引用中使用事件侦听器,那么这不是问题-但这不是必须的!
通常,是的,应该使用弱引用。但是首先,我们必须清楚“事件监听器”的含义。
在某些编程风格中,尤其是在异步操作的上下文中,通常将计算的一部分表示为对某个事件执行的回调。例如,Promise
[ 1 ]可能具有then
在上一步完成时注册回调的方法:
promise =
Promise.new(async_task) # - kick off a task
.then(value => operation_on(value)) # - queue other operations
.then(value => other_operation(value)) # that get executed on completion
... # do other stuff in the meanwhile
# later:
result = promise.value # block for the result
在这里,由登记的回调then
必须由强引用保留,因为promise(事件源)是唯一持有对该回调的引用的对象。这不是问题,因为承诺本身的寿命有限,并且在完成承诺链后将被垃圾回收。
在观察者模式中,主题具有从属观察者列表。当对象进入某种状态时,根据某种界面通知观察者。可以将观察者添加到主题中或从中删除。这些观察者并不存在于语义真空中,而是出于某种目的在等待事件。
如果此目的不再存在,则应将观察者从该主题中移除。即使使用垃圾收集的语言,也可能必须手动执行此删除操作。如果我们无法删除观察者,它将通过从主题到观察者的引用以及观察者引用的所有对象而保持活动状态。这将浪费内存并降低性能,因为仍将通知(现在无用的)观察者。
弱引用可以解决此内存泄漏问题,因为它们允许观察者被垃圾回收。当主题四处走动以通知所有观察者并发现对观察者的弱引用之一为空时,可以安全地删除该引用。或者,可以以允许主体注册清除回调的方式实现弱引用,该回调将在收集时删除观察者。
但是请注意,弱引用只是一个创可贴,可通过忘记删除观察者来限制损害。正确的解决方案是确保在不再需要观察者时将其删除。选项包括:
手动进行操作,但是容易出错。
在Java或using
C#中使用类似于资源的尝试方法。
确定性销毁,例如通过RAII习语。请注意,在具有确定性垃圾回收的语言中,这可能仍然需要从主体到观察者的弱引用才能触发析构函数。