The Weak Event Pattern is Dangerous

Summary

This article describes the motivation for the “weak event” pattern and explains some dangers associated with using it. Briefly: a weak event manager will not be able to differentiate between subscribers that are active and “in use” and subscribers that are out of scope, garbage collectable but not yet garbage collected. Such subscribers will have their event handler (or message handler) invoked even after they become garbage collectable (but not yet collected) and that can cause unexpected side effects.

Source code demonstrating this issue can be found at https://github.com/ladimolnar/Samples/tree/master/Sources/WeakEvents

The birth and death of a .NET object

The life cycle of an object in .NET has a quite a few complicated details. Fortunately, in most situations, these details can be ignored and a much simpler model can be assumed. There are some cases however when the complexity of what goes on under the hood in .NET matters. In those cases, not knowing the details can surprise you in ways that will negatively impact your application. We are going to look at one of those cases and it has to do with zombies.
When I think about the life cycle of a .NET object (a reference type) this is the simplified model that I normally have in mind:

  • The object is instantiated using the new keyword.
  • The object goes through a period when it is in use. That means the object can be accessed from somewhere in the code. Even if you don’t do anything with it, as far as .NET is concerned, if the object can be accessed by your code then it is in use.
  • The object is no longer in use. That means the object can no longer be accessed from any place in the code. Think a local variable that goes out of scope or a class instance after there are no longer any references pointing to it.

Zombies

If you want to go beyond this simple model, the best way to learn more is to learn about the garbage collector in .NET. Any good resource on that subject will teach you what you need to know about the life cycle of an object. There is one particular aspect of that life cycle that we’ll look at. There is a time interval after an object is no longer accessible from anywhere in the code and before the memory for that object is reclaimed by the framework.  Reclaiming the memory for that object is done by the Garbage Collector. In other words, there is a time interval after the object becomes garbage collectable and before the object is actually garbage collected. In that time interval the object still exist but it is not quite “alive”. An object in this state is sometimes referred to as a zombie object.

It is common that an object exists as a zombie for only a short period of time. That could be just until the Garbage Collector gets a chance to run next time. However, there is no set rule for how often the Garbage Collector runs. Basically it runs “when needed” and what that means is influenced by numerous factors. Also, just because the Garbage Collector runs, it does not mean that it will collect all garbage collectable (zombie) objects. The garbage collector makes a compromise between the need to keep the application memory set small and the need to run the garbage collection process as fast as possible. Some zombies will survive multiple garbage collections. In some cases, a zombie can last for a very long time – even for the remaining life of the application.

Resurrecting zombies

Why should you care about zombies? You shouldn’t of course. After all, you can no longer access the zombie object and as far as you are concerned the object is “gone”. That is, unless you are in possession of some magic that you can use to resurrect it. Then you should care.
That magic exists in the form of “weak references”. Sometimes that magic will be invoked on your behalf without you even realizing it and that is when complications can follow.
A weak reference in .NET is implemented by a class called WeakReference. A WeakReference can be used to reference another object in a way that will not prevent the garbage collector from reclaiming that object. If the referenced object becomes a zombie, the weak reference can resurrect it. Resurrecting a zombie means creating a regular (strong) reference to it. Since now there is a new reference to that object, as far as the Garbage Collector is concerned, the object is “in use” and no longer garbage collectable. Of course, after being resurrected, if all references to that object “go away” again, then the object will become a zombie again.

Why would you ever want to use a WeakReference and resurrect zombies? Without going into a lot of details here are a few reasons why you may want to take advantage of the class WeakReference:

  • You can implement a caching system using weak references. The idea is that you have an object that is expensive to create, uses a lot of memory and is seldom used by the application. Once created, you can provide access to that object via a weak reference. If the application is subjected to memory pressure this system will allow the memory used to be reclaimed. If the application is not subjected to memory pressures the object can be made available later without incurring the cost of instantiating it again.
  • You can instrument your code and setup a memory leak detector based on weak references.
  • You can implement the weak event pattern using weak references.

Classic events and memory leaks

Let’s leave zombies aside for a moment; we’ll get back to them soon. Let’s talk about the most common cause of memory leaks in .NET: the incorrect use of classic events. Any time an object C (client) subscribes to an event provided by another object S (service), object S will obtain a reference to object C. Any time when you write code like this:

service.SrvEvent += this.MyHandlerMethod;

the compiler generates code that will result in the event “SrvEvent” holding a reference to “this”. That makes sense, otherwise the event would not know how to invoke the correct event handler.

In many cases object C is an object with a short life span like a page or a control and object S is an object with a long life span like a service that lives throughout the entire duration of the application.

ClassicEvents2

The problem arises when the object C does not unsubscribe from the event. In that situation the event will retain its reference to object C and that will cause object C to leak in memory. Fixing this scenario is straightforward: the client code should at some appropriate moment unsubscribe from the events it subscribed to. What the “appropriate moment” is depends on the situation. For example, a control could subscribe to such events when loaded and unsubscribe from them when unloaded. There are cases however when finding the appropriate moment to unsubscribe is difficult and setting up the correct subscribing / unsubscribing mechanism can be a complex undertaking.

Weak references to the rescue

To address the case where finding the appropriate moment to unsubscribe is difficult, the “weak event” pattern was introduced. The idea behind it is to have a subscription mechanism that does not retain direct references to the objects that subscribe to events. Instead, the subscribers are referenced via a WeakReference. With a mechanism like this, if a subscriber omits to unsubscribe from events it can still be garbage collected after it runs out of scope. The implementations of this pattern differ from the classic event mechanism in ways other than just using weak references. For example the focus shifts from the concept of events to the concept of messages. Also, with classic events it is necessary that the client code has some knowledge about and access to the service that is providing the event. With messages, the client code only knows that certain messages are raised and may not know the identity of the service that publishes the messages.

The dangers of the Weak Event pattern

The problem with a system that implements the “weak event” pattern is that when using weak references one cannot differentiate between a subscriber that is active (in use) and a subscriber that is garbage collectable but was not yet collected – in other words a zombie. If the subscriber is a zombie, when the event is raised (or the message published) a weak event manager will inadvertently resurrect the zombie, call its event handler and then let the subscriber become a zombie again. Having the event handler of a zombie executed can lead to problems. Let’s consider as an example a UI control that subscribes to an event using a weak event manager. At some point that control will go out of scope. It will disappear from the screen and no place in the code will still have a direct reference to it. Regardless of that, unless the control instance is actually garbage collected, its event handler (or message handler) will still be called. At some point during the execution of your application you could have 10 instances of a control, one instance that is actually shown on the screen and 9 instances that are zombies. When the event they subscribed to is raised, one event handler will execute as expected. Also, nine other event handlers will execute and it is fair to say they will do so without the developer intending it. Imagine the event handler performing a database operation. Maybe charging a fee, or update certain data and hence overwriting another update that was just performed by the “legitimate” event handler. Or imagine the event handler taking other actions that are no longer valid because the context associated with such a zombie control is no longer actual. It is easy to see how this type of scenario can cause crashes, data corruption or other inappropriate actions like overcharging a client.

What is worse, depending on run time behavior and timing, a bug like this may not manifest in an application except in certain circumstances that are influenced by many factors like the amount of memory installed on a device, the memory used by the application, the timing of the event and so on. You can easily run in a situation where your application runs just fine for a long time and once in a while it crashes or corrupts data. Good luck debugging it!

Conclusion

Systems that implement the “weak event” pattern often advertise that they are solving the memory leak issues associated with classic events. Such systems may have other legitimate advantages but their attempt to solve the memory leak issue is a double edged sword. While it is true that such system will actually prevent memory leaks in scenarios where subscribers omit to unsubscribe, they will also open the door for subtle bugs caused by zombies having their event handlers called. The cure in this case may be worse than the disease. Do not believe statements that encourage you to use the weak event pattern as something that allows you to neglect unsubscribing from events. Always find a way to unsubscribe from events at some appropriate time. Occasionally that can be difficult to implement and involves putting in place complex code but that is better than the alternative.

This does not mean that I have a blanket recommendation against using weak event managers. Like I mentioned earlier they differ from classic events in other aspects and those differences can in some cases be beneficial. In any case, in situations where there are no clear advantages of weak events over classic events is better to use classic events. If you decide to use weak events just make sure that you do not neglect unsubscribing from events (messages).

Source Code and Demo Application

Source code demonstrating the dangers of the weak event managers can be found at:
https://github.com/ladimolnar/Samples/tree/master/Sources/WeakEvents

As a weak manager implementation the source code uses the messaging plug-in for MvvMCross.
See NuGet: MvvmCross.HotTuna.Plugin.Messenger, version 3.5.1.

Here is a screenshot of the demo application:

WeakEventsDemoAppScreenshot

What is new in XAML in Win 10 / UWP

General concepts

Adaptive UI

A set of techniques and features that facilitate creating UI that adapts automatically to different resolutions or application window size. Some of these techniques can be used to adapt the UI to any arbitrary condition (see custom triggers).

Effective pixels vs. physical pixels

When you write in your XAML something like Width=”100″ that will not set a width of 100 physical pixels. Instead it will set a width of 100 “effective” pixels. All the sizes and margins specified in a XAML file are expressed in effective pixels. The OS will choose how many physical pixels an effective pixel has based on the device type and screen size.

For example on a 7″ tablet with a resolution of 1920 X 1200 the OS will apply a scaling factor of 1.4 and to your XAML code the screen will appear as having a resolution of 1371.429… X 857.143… On monitor of 23″ with a similar resolution of 1920 X 1080, the OS will apply a scaling factor of 1.0 and to your XAML code the screen will appear as having a resolution of 1920 X 1080.

The idea is to make an element who’s size is set in effective pixels look roughly the same on different devices considering that different devices are placed at different distances from the viewer depending on their type and size. The result is that in general, this will allow a developer to write the same XAML regardless of the type of device or the size of the window where the application will run.

The golden rule in UWP UI sizing

As you design your UI, the margins and sizes of the UI elements (expressed in effective pixels) must be divisible by 4. Not an absolute rule but a good rule of thumb.

Handling Images

You may not be able to rely on effective pixels when dealing with images. You may need to provide different assets for the same image and let the OS pick the appropriate one based on naming conventions where the scale name is embedded in the name.

UI transition points

In some cases you can design your UI so that it has a smooth, continuous transition based on changing the size of your window. That is not always possible however. In some cases you will prefer to rearrange the UI when the screen size crosses a certain value. That value is called a transition point. In that case pick 3 transition points. Common practical transition points for the screen size are 5″, 8″, 13″.

The 6 Rs

These are design techniques that can be used to achieve a responsive / adaptive UI behavior.

  • Reposition
  • Resize
  • Reflow
  • Reveal
  • Replace
  • Re-architect

Guidelines are less prescriptive

From what I have read it seems that with UWP Microsoft decided they will be less prescriptive when it comes to UI/UX design guidelines. It is not clear to me in what degree will the rules be relaxed but there is a general tendency to allow developers to do “what is best” for their applications.

XAML Adaptive Techniques

Adaptive Triggers

You can trigger a visual state based on a new XAML syntax. This allows you to switch to a different visual state based on the window resolution (in effective pixels). You no longer need to write code behind and call VisualStateManager.GoToState.

<VisualStateGroup>
    <VisualState x:Name="wideState">
        <VisualState.StateTriggers>
            <AdaptiveTrigger MinWindowWidth="641" />
        </VisualState.StateTriggers>
        ...
    </VisualState>
</VisualStateGroup>

Visual State Setters

The XAML syntax available in the context of Visual States was improved. You can now set properties of XAML elements in a more concise form. The same effect was achievable in the past but then you needed to create a storyboard and use some verbose animation elements.

<VisualState.Setters>
    <Setter Target="filter.Margin" Value="12,12,12,0" />
    <Setter Target="filter.FontSize" Value="12" />
    <Setter Target="results.Margin" Value="12,12,12,30" />
    <Setter Target="noResults.Margin" Value="12,12,12,0" />
</VisualState.Setters>

RelativePannel

Writing adaptive UI brings new challenges. The new RelativePannel was specifically designed to help write adaptive UI. If you set the children of a RelativePannel in the right way (you will have to learn about the RelativePannel attached properties) they will adjust their position automatically even without any help from Visual States. Combining a RelativePannel and Visual States can increase the “adaptive-ness” of your UI even further.
Here is an example of using both a RelativePannel and Visual States:

<VisualState>
    <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="720" />
    </VisualState.StateTriggers>
    <VisualState.Setters>
        <Setter Target="button.(RelativePanel.RightOf)" Value="inputLabel" />
        <Setter Target="button.(RelativePanel.Below)" Value="title" />
        <Setter Target="avatar.(RelativePanel.RightOf)" Value="avatarLabel" />
        <Setter Target="avatar.(RelativePanel.Below)" Value="inputLabel" />
        <Setter Target="list.(RelativePanel.Below)" Value="avatar" />
    </VisualState.Setters>
    ...

Custom Triggers

XAML now allows for visual states to be triggered using a new syntax. For example:

<VisualState>
    <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="720" />
    </VisualState.StateTriggers>
    ...

You can also create your own custom state triggers by inheriting from StateTriggerBase and invoking SetActive at the appropriate moment. You can use such custom trigger in XAML:

<VisualState>
    <VisualState.StateTriggers>
        <WarningLevelTrigger WarningLevel="Critical" />
    </VisualState.StateTriggers>
    ...
    <VisualState.StateTriggers>
        <WarningLevelTrigger WarningLevel="High" />
    </VisualState.StateTriggers>
    ...
    <VisualState.StateTriggers>
        <WarningLevelTrigger WarningLevel="Low" />
    </VisualState.StateTriggers>
    ...

Question: Is there a potential for creating memory leaks here? Note that when implementing the custom trigger you do not know when it is getting out of scope (that is when the page that uses the trigger is unloaded). For example if you need your trigger to subscribe to an event of a global or long lived object, will your trigger class leak in memory? You could try to use an event subscription based on weak references (like the ones in MvvMCross) but then you will still have the event called after your page is unloaded and before the trigger instance is garbage collected. That means you will call SetActivate on the trigger after the page where the trigger lives was unloaded. Would calling SetActivate at such time cause an error or crash?
Would be nice if the trigger had something like a Loaded / Unloaded event.

XAML views

This is a new technique that allows you to provide alternate versions for your pages. These alternate versions will be used automatically by the framework based on the device type where your application is running. For example let’s say that you have a page called MainPage.xaml. Using XAML views you can create one or more alternate versions of this page. You will have the regular file <Root>\MainPage.xaml (this is the default xaml file). For the mobile version you will create another file <Root>\DeviceFamily-Mobile\MainPage.xaml. Note the naming convention of the folder.

When using views you will not be allowed to provide a separate code behind for the alternate versions of your view. The code behind that is used for all versions of the view is still the one in file <Root>\MainPage.xaml.cs. With this technique you will only be able to vary the XAML file.

Question: It is not clear how useful this is if you cannot vary the code behind. What if the xaml is fundamentally different? Won’t keeping one code behind file lead to dirty code?

Question: If the two versiond of the XAML are fundamentally different to the point where you would othervise use different view models, how would you handle that situation? There are techniques that can be used to set the view model in XAML but they cannot be applied in any situation.

Question: Can you use this technique for user controls or custom controls?

Links