We are using Model View Controller (MVC) architectural pattern to build mobile apps and games. MVC helps us decouple views from the rest of the application, so we can change the user interface without changing any model or logic in our app.

How do we update the View (user interface) when a Model (data) is changed? The View can query the model periodically and if something has changed updates itself. But it will consume lots of cpu cycles needlessly if the model is not updated very often. A better solution will be registering for model update events and being notified whenever the model is updated.

So the question is “How can an object (the model in our case) notify other objects of state changes without being dependent on their classes?”. The answer is the Observer design pattern.

This post will spot light on potential problems that may arise while implementing Observer pattern in your software projects.

The example I am going to use is a real life scenario taken from one of our multiplayer turn-based games. Every player in the game should make their moves in a certain amount of time (20 seconds for example). When turn changes a new timer is started for current player and the UI is updated every second to display remaining seconds.

Observer Pattern

Below is a simplified version written in Java.

CountDownObserver is the interface that must be implemented if a class wants to be notified about countdown events.

CountDownTimer is our subject. It calculates the elapsed and remaining times and notifies observers every second.

CountDownTimerView is the observer. It implements CountDownObserver interface. Its responsibility is to render the view.

CountDownTimer (the subject) knows nothing about the views that are displaying remaining seconds. Before getting notified about countdown changes, views (the observers) must register themselves to the CountDownTimer.

So what can possibly go wrong?

In a runtime where memory is managed automatically like Java or C#, the subject holds a strong reference to the observers, keeping them alive. The strong reference from the subject (the observed object) to the observer, prevents the observer (and any objects it references) from being garbage collected until the observer is unregistered. A memory leak happens, if the observer fails to unregister from the subject when it no longer needs to receive notifications. In our countdown timer example if observers do not call unregister when they become invisible, they will receive notifications and waste CPU cycles updating invisible UI elements. This issue is called Lapsed Listener Problem.

If you implement the observer pattern in C++ (no garbage collection, memory is managed manually) and you call delete on one of the observers without unregistering it from the subject then you are in trouble. Now you have a dangling pointer that points to invalid data. Whenever the subject notifies its observers a segmentation fault will be raised.

Always unregister your listeners unless they are interested in receiving notifications.

Another approach for dealing with lapsed listeners (observers) is to use Weak References.

The below implementation of CountDownTimer uses weak references to keep track of registered observers. If the only references to an instance are weak references, then the instance becomes a candidate for Garbage Collection. In contrast to strong references, weak references don’t prevent garbage collector to finilize the instance and reclaim its memory.

Implementing the observer pattern by using weak references requires one more step. Now we have to find an object that has the same lifecycle as the observers and have hold a strong reference to the observer. When the strong reference holder object is garbage collected, our observer will be a eligible for garbage collection.

There is one more thing to notice; since the observers are stored in a LinkedList, registering and unregistering observers is not thread-safe. Most GUI implementations (and game engines) run on single thread, so we don’t think of multithreading in this GUI example.