K. Gadd (2013-11-07T22:42:57.000Z)
domenic at domenicdenicola.com (2013-11-17T17:50:17.438Z)
I'll try and restate an example I used before. Let's say you're building a somewhat simple tab-based application, where each tab represents a different 'mode'. In this case, you need a higher level coordinating object that manages the tab bar, and it needs to own references to all of the active modes in order to manage the tabs. Similarly, each mode may need to have a reference to the coordinator in order to interact with it - like by setting the title of the tab, or opening a new tab containing another mode - and it may need to have references to other modes in order to communicate (like if one tab opens a tab containing related information). In a model like that, you can trivially end up with object cycles. All the references (going in every direction) are strong, so to successfully collect a mode you need to ensure that every reference to it from live objects is released. This is problematic because it means that, for example: If Mode A opens a new tab containing Mode B, and Mode A wants to respond to things happening in Mode B, Mode A now has to register some sort of event listener on Mode B. This means that Mode A and Mode B have now formed a strong cycle: Mode B's event listener list contains a strong reference to Mode A (to fire the event notification), and Mode A has a strong reference to Mode B in order to be able to respond to those events. Now, if you close Mode B, the coordinator gets rid of the tab and drops its strong references to Mode B - the tab is 'gone' - but there's no trivial way to ensure that everyone else holding a reference to it is dead. If multiple tabs interact in this way it's possible for a huge graph of objects to be hanging off one live tab. Eventually, if you close enough tabs you might 'free' the graph, but if you aren't careful the coordinator itself can end up keeping dead tabs alive, with things like event listener lists. In the real apps I've worked on like this, the solution was some form of weak references - making event listeners own weak self-references instead of strong self-references, so that in normal circumstances event subscriptions expire along with the subscriber, along with using weak references directly in cases like 'tab a spawns tab b' in order to ensure that you aren't keeping an object alive just to monitor it. IIRC, cycles like this are already common in some DOM scenarios, where event subscription and such result in big graphs of objects hanging off a single live object? I've certainly run into it in a few cases, where an event listener hanging off an <audio> element caused a huge graph of objects to leak, and I had to manually remove the event listeners at an appropriate time (and finding an appropriate time can be difficult!)