In general, yes, weak references should be used. But first we have to be clear about what you mean by “event listeners”.
Callbacks
In some programming styles, especially in the context of asynchronous operations, it is common to represent a part of a calculation as a callback that gets executed on a certain event. For example a Promise
[1] may have a then
method that registers a callback upon completion of the previous step:
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
Here, the callbacks registered by then
have to be held by strong references, as the promise (the event source) is the only object holding a reference to the callback. This is not an issue as the promise itself has a limited lifetime, and will be garbage collected after the chain of promises is completed.
Observer Pattern
In the observer pattern, a subject has a list of dependent observers. When the subject enters some state, the observers are notified according to some interface. Observers can be added to and removed from the subject. These observers do not exist in a semantic vacuum, but are waiting for events for some purpose.
If this purpose does no longer exist, the observers should be removed from the subject. Even in garbage-collected languages, this removal might have to be performed manually. If we fail to remove an observer, it will be kept alive via the reference from the subject to the observer, and with it all objects that the observer references. This wastes memory and degrades performance as the (now useless) observer will still be notified.
Weak references fix this memory leak, as they allow the observer to be garbage collected. When the subject goes around to notify all observers and finds that one of the weak references to an observer is empty, that reference can be safely removed. Alternatively the weak references might be implemented in a way that allows the subject to register a cleanup callback that will remove the observer upon collection.
But note that weak references are only a band-aid that limit the damage by forgetting to remove an observer. The correct solution would be to make sure that an observer is removed when no longer needed. Options include:
Doing it manually, but that's prone to errors.
Using something akin to try-with-resource in Java or using
in C#.
Deterministic destruction, such as via the RAII idiom. Note that in a language with deterministic garbage collection, this might still require weak references from the subject to the observer in order to trigger the destructor.