addEventListener in mediator with weak reference on the view fails; with strong reference succeeds...why?

darrendb's Avatar

darrendb

15 Apr, 2010 09:42 PM via web

In a mediator, when i try to listen for an event dispatched from its view, it works as expected if i use this syntax:

`viewComponent.addEventListener(CardEvent.CARD_DROP, onCardDrop_cardView);`

However, if i use weak reference syntax, the eventHandler is never fired:

`viewComponent.addEventListener(CardEvent.CARD_DROP, onCardDrop_cardView, false, 0, true);`

This isn't really a show stopper, but I'm curious why this is. I assume its something to do with the internal working of SwiftSuspenders and the injection.

I've tried using both the inherited refrence to the view, viewComponent as well as an injected strong-typed reference to the view cardView with the following injection:

`[Inject] public var cardView:CardView;`

there doesn't seem to be any difference, both fail if i try to use weak reference syntax.

Thanks!

d

  1. Support Staff 2 Posted by Shaun Smith on 16 Apr, 2010 12:36 PM

    Shaun Smith's Avatar

    Is the mediator removed before the event fires? That's the only reason I can think of as to why the weakly referenced listener would fail. It's almost certainly not related to SwiftSuspenders/Injection though.

    Are you wiring the listener up during onRegister()? Perhaps you could put a trace in both the listener and onRemove() and see which fires first.

    Mediators are held onto by the MediatorMap for as long as their corresponding view components are on stage. During this time weakly referenced listeners should function normally.

  2. 3 Posted by Jason Dias on 16 Apr, 2010 03:30 PM

    Jason Dias's Avatar

    Possibly un-related but, have you considered using the eventMap to map your events?

    Instead of this
    viewComponent.addEventListener(CardEvent.CARD_DROP, onCardDrop_cardView);

    you would do this
    eventMap.mapListener(viewComponent, CardEvent.CARD_DROP, onCardDrop_cardView);

  3. 4 Posted by darrendb on 21 Apr, 2010 05:35 PM

    darrendb's Avatar

    @Shaun: Thanks for the response. Neither the Mediator nor its viewComponent are being removed, ever. At least, not deliberately, I never try to destroy the Mediator, and I never remove the viewComponent.
    Yes, I wire up the events in the Mediator's onRegister(). I tried your idea of adding a listener to viewComponent's Event.REMOVED, and a trace in both that listener and the onCardDrop_cardView listener. I would see my onCardDrop trace, then an onRemove trace. Since I never remove the viewComponent, (it never disappears from the stage/screen) I don't understand why that REMOVE event is firing. Which is why i was wondering if it was something behind the scenes with the Injection of the viewComponent as cardView:CardView.

    @Jason: Also, thanks for the response! I hadn't used the eventMap.mapListener syntax before. I noticed that it uses weak references by default. I'll have to read up on the benefits of using that over addEventListener. However, there was no change in behavior using that syntax. I like it though, I will start to use it in the future.

    So i have several events mapped now:
    eventMap.mapListener(cardView, CardEvent.CARD_DROP, onCardDrop_cardView, CardEvent); eventMap.mapListener(cardView, CardEvent.DRAG_STARTED, onDragStarted_cardView, CardEvent); eventMap.mapListener(cardView, CardEvent.DRAG_STOPPED, onDragStopped_cardView, CardEvent); eventMap.mapListener(cardView, CardEvent.LAST_CARD_COMPLETE, onLastCardComplete_cardView, CardEvent); eventMap.mapListener(cardView, Event.REMOVED, onRemoved_cardView, Event);

    That above code will eventually fail, sometimes after 3 drag-n-drops, sometimes 6, its not consistent.
    But, if I add ,false, 0, false to ANY single one, it works. Again, it seems that the reference to the viewComponent, provided by the Injection, gets removed and recreated, and listener(s) are lost. I know its possible that I might be observing a symptom of some other problem, although I'm not sure what that might be.

    For now I'm up against a deadline, so I'll just have to stick with what works and keep an eye out for some underlying cause.

    Thanks! I'm just striving for understanding and best-practice-compliance. I'll get there eventually, hopefully!

    - d

  4. Support Staff 5 Posted by Shaun Smith on 21 Apr, 2010 06:07 PM

    Shaun Smith's Avatar

    Just to clarify, did you add a trace to the Mediator's onRemove hook, or to a listener attached to the view's Event.REMOVE? I was hoping to determine if the Mediator itself was being removed - in which case the onRemove hook would be called.

    As you noticed, the EventMap uses weak references by default - this is safe because as long as the MediatorMap is holding on to the Mediator, these listeners will function correctly. So, it seems like the Mediator is being removed, and the only reason for this would be because the view component is firing an Event.REMOVED_FROM_STAGE event.

    By using strong references, you are forcing the view instance to hold on to the Mediator, which is why it works as expected.

    Something, somewhere, seems to be telling the MediatorMap to remove the mediator - perhaps by temporarily removing the view component from the stage. You mentioned something about drag-n-drop.. perhaps the view component is being lifted off of the stage somehow?

    Side note: There are two advantages to using the EventMap over addEventListener():

    1. It keeps a reference to each listener for easy removal later. By default, when a Mediator is removed, the EventMap is instructed to remove all listeners automatically, so you don't have to.

    2. It allows for stronger type-safety than addEventListener(). By specifying a concrete Event class (4th param, optional) it can filter out events that don't match the desired concrete Event class even if Event's string "type" property is the same. I.E. CustomEvent.BOOM can be distinguished from OtherCustomEvent.BOOM even though the BOOM constant evaluates to "boom" for both. This is more important for the shared EventDispatcher than for view components, but it's still a nice safe-guard.

  5. 6 Posted by Thomas Troelsen on 16 May, 2010 09:36 AM

    Thomas Troelsen's Avatar

    I have experienced the same problem with eventlisteners removed by garbage collection. My setup:

    Flash Builder 4
    Robotlegs 1.1.0b7

    I got a custom spark Panel (view:DescriptionView) as view injected into my mediator.(DescriptionMdt).

    in the mediator onRegister()

    super.onRegister();
    eventMap.mapListener( view, MouseEvent.CLICK, onClick );
    trace("registered, " + view.hasEventListener(MouseEvent.CLICK);

    in mediator onRemove

    super.onRemove();
    trace("removed, " + vierw.hasEventListener(MouseEvent.CLICK);

    im mediator onClick(e:MouseEvent)

    view.doSomething();

    in MainContext

    mediatorMap.mapView( DescriptionView, DescriptionMdt );
    // contextApp is contextView as Application contextApp.addElement( new DescriptionView() );

    console output registered, true


    So far so good, but when I run the application, the eventlistener stop working after i used it 5-6 times (or maybe in a short time interval, can't figure it out). When i click the view initially the doSomething() is called, but suddenly it stops working.

    When i use strong references

    eventMap.mapListener( view, MouseEvent.CLICK, onClick, MouseEvent, false, 0, false );

    The problem does not occur, so it seams clear that the problem arise from garbage collection. Please note the onRemove is not called at any time in this test.

    Any help appriciated - keep up the good work Robotleggers!

  6. 7 Posted by Thomas Troelsen on 16 May, 2010 10:14 AM

    Thomas Troelsen's Avatar

    Please note typo "vierw" in onRemove is not present in code, just in this example.

    I will just add that the DescriptionView is not drag-and-dropped in this example, and is not removed from the display list at any time.

    I am a bit weary of memory leaks when using strong references, so please enlighten me if you find the solution :-)

    Thanks!

  7. Support Staff 8 Posted by Shaun Smith on 16 May, 2010 01:01 PM

    Shaun Smith's Avatar

    Hi Thomas,

    Just double-checking, but are you sure that you are holding on to your context? I've seen people make this mistake quite a lot:

    public class MyApp extends Sprite
    {
        public function MyApp()
        {
            // This is wrong - the whole context will be free for GC
            new MyAppContext(this);
        }
    }
    

    You have to hold on to the context instance or it will be free for GC:

    public class MyApp extends Sprite
    {
        protected var context:MyAppContext;
        
        public function FloodRunner3()
        {
            context = new MyAppContext(this);
        }
    }
    

    Though, I just noticed that you're using Flex. In that case, are you instantiating the context like this:

    <fx:Declarations>
        <contextns:MyAppContext contextView="{this}"/>
    </fx:Declarations>
    
  8. 9 Posted by Thomas Troelsen on 18 May, 2010 08:14 PM

    Thomas Troelsen's Avatar

    Hi Shaun.

    Thank you for your quick reply.

    Yes I am referencing my context instance, so it should not be removed for garbage collection. I also have tryed with both referenced (privar var _descriptionView) and unreferenced anonymous (new DescriptionView()) views.

    But I will look further into the problem, since it might be a Flex Spark framework issue - I have only use Flex 4 framework for some months, but the Flex 3 framework had problems with garbage collection in the old Tween class. When I remake my application using pure Flash events (view.addEventListener) but set the flag to true in "use weak references", the MouseEvent.CLICK listener also stop working after a short amount of time.

    A bit strange since I reference both my context and my view - maybe something is going on inside the Spark Panel that I don't understand?

    Anyway i found another issue in my code, but that' unrelated to the above issue with weak references. If I call super() in the onRegister() or onRemove() mediator functions, the rest of the code is not executed (in the sub class), so now I just override them without calling.

    I'll report back if I bump into a solution - for now I'll just use strong references.

    Regarding you question to my MXML file, then I'll check it out, since i do not initialize my context in the MXML file, but use a helper class to do it (called EntryPoint) - I will see if it has a reference in the MXML file.

    :-)

  9. 10 Posted by Thomas Troelsen on 25 May, 2010 10:09 AM

    Thomas Troelsen's Avatar

    Hi Shaun.

    I think the problem is resolved. I followed your advice and checked my properties. The context and the application was in class scope, private var, but my EntryPoint class (a dummy as file used to get away from MXML as fast as possible :-) was a local var, and therefore due to garbage collection.

    In the last test i used Flash Player 10.0.x, and the reference was probably garbage collected after a short while, which explain why the click event fired the first few times.

    I just updated to Flash Player 10.1.52.14, and now the reference is garbage collected instantly as soon as i start up the application - so the click event does not even fire once.

    Therefore I am not 100% sure if it was a Flash Player 10.0.x bug, but I believe it is the above explanation.

    Thanks for the help, and keep up the great work!

    Thomas

  10. Shaun Smith closed this discussion on 20 Jun, 2010 04:14 PM.

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