mediator removal issue

james's Avatar

james

11 Oct, 2011 06:25 PM

Hi,

I've noticed that there's a delay in removing a Mediator once it's view has been removed and it's REMOVED_FROM_STAGE event has fired.

Up until now i've simply relied on the in built mechanism of the Mediator, to clean up the mapped listeners of their view and this has worked a treat. In simplfied terms, i've now got a situation where one view is removed and another is created, whose mediator then dispatches an event, that's being caught by the old views Mediator, as it's yet to be removed completely.

So the flow is,

ViewA gets destroyed and removed from display list
ViewA fires REMOVED_FROM_STAGE event (if i add a listener directly to its Mediator, the event is caught immediately)

ViewB is created and added to the display list
ViewB's mediator is created and dispatches an event to set in motion part of the initialisation of ViewB

ViewA's Mediator catches the result of that event and calls a function on ViewA
But ViewA has been destroyed so chucks an error

ViewA's Mediator is then destroyed

So the problem i have is due to the fact that the Mediators don't appear to be removed immediately when the REMOVED_FROM_STAGE event of their view is fired and so they're hanging around for a split second (possibly till the next frame?) and as their eventMap has not yet been automatically cleared, they're still receiving and proxying events.

So i realise that i could quite easily add a listener in the Mediator for the views REMOVED_FROM_STAGE event and clean it up myself, but this seems to slightly defeat the point of the simplicity and elegance of the Mediators automatic clean up. Not to mention all the extra code this could potentially create on a larger project. Or am i simply just doing something wrong?

Cheers,

James

  1. Support Staff 1 Posted by Till Schneidere... on 12 Oct, 2011 10:42 AM

    Till Schneidereit's Avatar

    Hi James,

    that is an interesting problem - and one that's so obvious that I'm
    surprised no-one else seems to have stumbled over it before (including
    myself).

    The reason for mediators not being removed immediately is that we
    can't detect whether a display object is really removed from stage or
    only re-parented to another part of the display list. To work around
    that, we store the mediators for removed views in a list and only
    really remove them in the next frame. If they are only reparented,
    they get removed from that list immediately.

    I think the best way to work around this would be to set some sort of
    flag on the mediator, but that's not too small a change, so we'll
    probably not do it for Robotlegs 1, because version 2 should be out in
    a couple of months. Therefore, I'd suggest you use the workaround you
    described.

    hope that helps,
    till

  2. 2 Posted by james on 12 Oct, 2011 10:54 AM

    james's Avatar

    Hi Till,

    thanks a lot for the quick reply.

    I've been using RL for over 18 months now and thought exactly the same thing when i came across the problem. It's what made me wonder whether it was something i was doing wrong, as i was surprised i've not come across it before.

    I had a poke around in the source and noticed that they're being removed once an ENTER_FRAME is run. Now that you've explained why, that makes complete sense.

    The flag sounds like a good idea, as it could just be set when the view and Mediator are mapped if you know the view won't be re-parented.

    Thanks again,

    James

  3. Support Staff 3 Posted by Till Schneidere... on 12 Oct, 2011 11:03 AM

    Till Schneidereit's Avatar

    I've created a ticket in the robotlegs github project so that we don't
    forget about the issue. Thanks for bringing it to attention.

  4. 4 Posted by james on 12 Oct, 2011 11:09 AM

    james's Avatar

    Nice one, thanks Till. Glad i could be of help, RL really is a pleasure to work with!

    This is probably pretty obvious, but i'll post it here just incase anyone else comes across this issue. So much for my rant about extra code...

    I've just stuck this line of code in the onRegister method of the offending Mediator,

    eventMap.mapListener( view, Event.REMOVED_FROM_STAGE, viewRemovedHandler, Event );

    Then i simply clear the _eventMap in the handler,

    private function viewRemovedHandler( event:Event ):void
    {

     _eventMap.unmapListeners();
    

    }

  5. Support Staff 5 Posted by Stray on 12 Oct, 2011 11:11 AM

    Stray's Avatar

    Hi James,

    Another quick fix for that specific mediator would be to add a guard clause in your handlers that checks that the view.parent != null.

    eg:

    protected function someHandler(e:Event):void
    {
        if(viewHasLeftTheStage)
        {
            return;
        }
    
        ... carry on and do stuff
    }
    
    protected function get viewHasLeftTheStage():Boolean
    {
        return (view.parent == null);
    }
    

    Or - you could create a version of the event map to put this clause around your handlers automatically. I'd probably go that route as it's most composition friendly...

    To implement that, you'd override your eventMap setter in the mediator:

        override protected function get eventMap():IEventMap
        {
            return _eventMap || (_eventMap = new ViewOnStageDependentEventMap(eventDispatcher, viewComponent));
        }
    

    viewComponent is what the mediator generically uses to refer to your view internally (as a DisplayObject).

    Then, you'd just add this check to your special event map (which has stored the viewComponent property that you passed it) - by overriding 'routeEventToListener':

    protected function routeEventToListener(event:Event, listener:Function, originalEventClass:Class):void
    {
        if (event is originalEventClass && viewComponent.parent)
        {
            listener(event);
        }
    }
    

    That should be all that is required... if it's a bit befuddling let me know and I can whip it up into a utility on github (with tests) today.

    Nice find - we like these corner cases (especially, as Till pointed out, during the pre-release phase for RL2!)

    Stray

  6. Support Staff 6 Posted by Stray on 12 Oct, 2011 11:12 AM

    Stray's Avatar

    Ah... and what you have will work as well!

    Stray

  7. 7 Posted by james on 12 Oct, 2011 11:27 AM

    james's Avatar

    Thanks Stray, that makes sense and a least i don't have to keep implementing the same logic throughout my offending Mediators.

    So just to clarify, i subclass Mediator and override the eventMap accessor and then subclass EventMap and override the routeEventToListener method?

    James

  8. 8 Posted by james on 12 Oct, 2011 11:54 AM

    james's Avatar

    One last thing, where would be the best place in the EventMap subclass to clean up the reference to the viewComponent? Override unmapListeners() ?

    James

  9. 9 Posted by Stray on 12 Oct, 2011 12:29 PM

    Stray's Avatar

    Exactly :)

  10. 10 Posted by Stray on 12 Oct, 2011 12:30 PM

    Stray's Avatar

    Hi James, that shouldn't be necessary - as the eventMap is only a property of the mediator, and the mediator will be GC'd, you should be fine without explicit clean up.

  11. 11 Posted by james on 12 Oct, 2011 12:36 PM

    james's Avatar

    Cheers Stray, that's all now up and running. Thanks again for your help.

    James

  12. Ondina D.F. closed this discussion on 01 Nov, 2011 05:02 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac