In part 4, I describe the best practices for memory management in Xamarin apps that I concluded after a lot of searching the internet and applying them to my app.
NOTE: Just a quick reminder that the presentation this series is based on was created in September, 2013. Things have improved since then. I will occasionally add notes with updates when appropriate.
Before starting, I will put the disclaimer out there that this list is comprised of recommendations that were gathered from a lot of different sources on the internet. I haven’t found a single source for all the recommendations I’m going to share with you. So, your mileage may vary.
You should also use the using statement whenever you instantiate new IDisposable-implementing object instances.
Next, either use Weak References for class-level properties or explicitly assign null to the properties in your Dispose method.
Also, you should only add event handlers to views when they are being displayed, then immediately remove the handlers when the view is removed.
Finally, use named methods instead of Lambda expressions when assigning an event handler. Otherwise, you won’t have a reference to the handler later when you’re finished with it and need to remove it.
Lets take a closer look at each of these recommendations.
The first recommendation was implementing the IDisposable interface. The code example on the left is the default implementation of the interface’s sole member, the Dispose() method, in the Xamarin framework.
As the code comments point out, this implementation doesn’t have to be (and shouldn’t be) overridden in your classes. Instead, a virtual Dispose method with a boolean “disposing” parameter is defined. This is the method you should override in your custom classes. You should then assign null to any class-level properties you added to your custom class, but only AFTER calling those property references’ Dispose method if they also implement the IDisposable interface.
You should also always pass a value of true for the “disposing” parameter if you are calling another object instance’s Dispose method. This tells the base-class implementation to also dispose of any related managed and unmanaged resources.
The only time this parameter should be false is when the method is called from the finalizer. The finalizer is the final step in freeing an object instance. At this point, all of the instance’s referenced instances should have already been dereferenced, so the Dispose method uses the boolean parameter to know when it should skip any attempt to reference anything.
The next best practice that I mentioned was the using statement. Again, the using statement can only be used with objects that implement the IDisposable interface. You’ll get a compile-time error if you attempt to use the statement with a class that hasn’t implemented the interface.
The using statement is referred to as a convenience method because the compiler basically replaces it with try-finally blocks. The allocation and variable assignment is performed in the try block, and the call of the Dispose method is made in the finally block. This means the instance’s Dispose method will always be called if the instance was successfully created, even in the event of an exception being thrown while the instance is in use.
Best practice number three is to use the WeakReference class when assigning class scope properties. Instances of the WeakReference class are ignored by the garbage collector when the parent instance is being considered for eligibility to be released from memory.
If you prefer the old skool way, you can directly declare and instantiate the instance type as a class level property, then explicitly set its value to null when it is no longer needed. This is a riskier approach in my opinion, as it means you must always insure that the property is set to null even in the event of an unhandled exception or other unexpected behavior.
Up next is managing event handlers. When you assign code to be executed when an event is fired, you’re actually creating a reference to the object that code belongs to. That object may never become eligible for garbage collection if you never remove the listener.
When event handlers are used with user interface components, a great place to add any needed handlers are just before the UI component is added to the screen. And just the same, a great place to remove them is when they are removed from the screen.
Take note that I’m accomplishing this in the provided code sample by adding a call to the AddEventListeners method in the ViewWillAppear method, which is called by iOS when a UI component is about to be added to the display. I also added a call to the RemoveEventListeners method in the ViewWillDisappear method, which is called by iOS when a UI component is about to be removed from the display. We’ll see the implementation of those methods in a second.
The last best practice recommendation I have for you is to use named event handler methods instead of anonymous Lambda expressions. Lambda expressions are fine when you’re adding an event listener. Its when you need to remove them that they are a problem, because you usually don’t keep a reference to them around.
By using a named method as your event handler, its just as easy to remove them as adding them, as you can see in the provided code sample.
In the final part of this series, I conclude with the results seen when profiling the XNotes app again after I implemented the recommendations I’ve presented. What was the result? Come back soon, or subscribe to receive future posts, to find out!