Click here to Skip to main content
15,884,629 members
Articles / Mobile Apps / Xamarin
Tip/Trick

Publish/Subscribe Pattern for PCL using Weak References

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
11 May 2014CPOL3 min read 13K   5   1
A Pub/Sub implementation that is PCL compatible for Xamarin iOS and Android

Introduction

The pub/sub pattern is a reoccurring pattern used by almost all non trivial application. Weak references come in very handy to avoid zombie subscribers causing memory leaks, wasted CPU cycles and other nasty bugs.

The following code example shows a WeakSubscriptionCollection class that uses weak references and can be used in Portable Class Libraries (PCL). I developed the code for a Xamarin project to implement an application for iOS and Android. (This code can be used with profile 7 or profile 78.)

Background

The pub/sub pattern is a pattern often used for messaging components that want to notify other components if a change in the system happened. The basic idea is that a subscriber subscribes to a publisher by providing a callback function that the publisher stores until the publisher wants to notify the subscriber of a specific state change in the system. So the main functionality of the publisher is to keep a list of subscribers and to allow subscribers to add and remove subscriptions to the list and to call subscribers on the provided callback method when a specific state change happened.

In a system with a garbage collector such as .NET or Java, an object is automatically removed from memory (garbage collected) if no other object holds a (strong) reference to that object. Weak References can be used to hold a reference to an object that will still allow garbage collection if there is no strong reference to that object.

By using weak references for the list of subscribers in a pub/sub implementation, subscribers can be collected if they are only referenced by the list of subscriptions of the publisher and therefore avoiding zombie objects being referenced by the publisher. In addition, the publisher can detect if a target of the weak reference is no longer alive and remove the dead's object subscription from its list of subscribers to keep the list clean with only alive subscribers.

Using the Code

Callbacks or Delegates in C# cannot be weak referenced. The reason is that the C# compiler will generate a new instance of the callback when passed to the subscriber. If the only reference that holds on to the passed in delegate is a weak reference, the garbage collector will collect it even though the target of the delegate is still alive. Therefore we have to perform a simple trick. We need to weak reference the target of the callback and store the information about the method on the target that should be called.

Please note that the code is simplified and synchronization objects, try/catch/finally blocks and some cleanup code has been omitted for clarity!

C#
List<WeakReference<object>> listOfSub;        //That's the method we need to call
var methodInfo = onChange.GetMethodInfo();
if (!_subscriptions.TryGetValue(methodInfo, out listOfSub))
{
listOfSub = new List<WeakReference<object>>();
_subscriptions.Add(methodInfo, listOfSub);
}
//The object to be called 
listOfSub.Add(new WeakReference<object>(onChange.Target)); 

The central piece of most (or probably all) publisher is the list of subscribers. In order to allow async execution and thread safety, I implemented method pairs (xyzBegin and xyzEnd) for all the operations on the collection. A possible optimization could be to avoid the locks by using Concurrent collections or Immutable lists.

The main operations of the publisher are: Add Subscribers, Remove Subscribers and Invoke Subscribers:

C#
public class WeakSubscriptionCollection
{
    public void AddBegin(Delegate onChange){...}
    public void AddEnd() {...}
    public void InvokeBegin() {...}
    public void InvokeEnd(object[] args) {...}
    public void RemoveBegin(Delegate onChange) {...}
    public void RemoveEnd() {...}
}

The idea is that any publisher class can use the SubscriptionCollection to manage the list of subscribers. This makes the Publisher very simple to implement.

A publisher could be implemented like this:

C#
public class PubExample
{
    public void Subscribe(Action<int,int> onChange)
    {
        _subscribers.AddBegin(onChange); //start adding
        if (_subscribers.Count == 1) //this is the first subscription, do something special
        {
            _service.Connect( 
                () => _subscribers.AddEnd(), //called when connection is established 
                     onChangeInternal); //called by the service if a change happens 
        } 
        else //already connected to service, just callback with current data
        {                        onChange(currentInt1,currentInt2);
            _subscribers.AddEnd();
        }
    }
    private void onChangeInternal(int val1, int val2)
    {
        _subscribers.InvokeBegin();
        currentVal1 = val1;
        currentVal2 = val2;
        object[] args = { val1, val2 }
        _subscribers.InvokeEnd(args);
    }
    public void Unsubscribe(Action<List<PartyEntity>, List<PartyEntity>, List<Guid>, bool> onChange)
    { 
        _subscribers.RemoveBegin(onChange);
        if (_subscribers.Count == 0)
            _service.Disconnect();
        _subscribers.RemoveEnd();
    }   

A subscriber subscribing to the publisher could be implemented like this:

C#
public class Subscriber
{
    private PubExample publisher;
    private void onChange(int val1, int val2) 
    {
        //This is called on callback
    }
    public void Run()
    {
        publisher.Subscribe(onChange);
        ...
    } 
    public void Terminate()
    {
        publisher.Unsubscribe(onChange);
    } 
} 

What I want to point out is that the Subscriber can get garbage collected if no other object is referencing it even if the Subscriber did not unsubscribe. If the subscriptions would not weak reference the target, the Subscriber would never be collected because of the subscription.

Points of Interest

The WeakSubscriberCollection is highly reusable and hides a lot of the code to manage the collections and invoking of the subscribers. That leads to a very simple implementation of the publisher. As mentioned earlier, the code is simplified for clarity.

History

  • Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Architecting, designing and implemention of software projects. When not stuck at the keyboard sails, climbs, snowboards or does other outdoor activities.

Comments and Discussions

 
QuestionEntire source? Pin
Steve Macdonald17-Jun-14 2:10
Steve Macdonald17-Jun-14 2:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.