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.
We’re now going to review the best practices when it comes to memory management. These approaches are what I implemented in my app to resolve its problems.
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.
So here we go: First, you should implement the IDisposable interface on any custom class that contains properties or event listeners.
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.
Coming Up…
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!
Reihaneh says
Hello,
Thanks for your great article, I just have a couple of questions. Hope you’d have the time to answer them.
1. In your first recommendation, what is CloseHandle() method? and what does it do exactly? Should I write it myself?
2. In the “use named event handler” recommendation, you have overridden the ViewWillAppear() and RemoveEventListener() methods, but since I’m using Xamarin.Forms, there are no such methods available. So, can I use OnApperaing() method instead of ViewWillAppear()? and as for RemoveEventListener(), will it be enough if I just remove the listeners?
Thanks in advance
Tommy Baggett says
Hi Reihaneh,
Thanks for the kind words regarding the article. As for your questions, the CloseHandle() method is an OS call to release a handle returned when opening a file. This was only an example, and for a Xamarin.Forms project, would be found in your platform-specific code if you had to use OS-specific functionality for some reason. You would more likely use .net file I/O methods in your cross platform code with a using statement, for example “using (var sr = new StreamReader(“testfile.txt”)) { … }”. In that case, the disposal will be taken care of for you by the using statement. See the MS docs on the using statement if you’re not familiar with that.
For your second question, yes you are correct, you would add and remove listeners in the OnAppearing and OnDisappearing methods, respectively. Be aware though that these methods are currently only available for Page-derived classes, not View-derived ones, despite them being requested for some time. Also, yes, you can remove them directly instead of creating a separate method to add and remove them. That was done to provide consistency across view classes to make the code easier to maintain over time. Having them allows you to perform a search for the methods to quickly locate where listeners are being added and removed.
Hope this info helps!
Reihaneh says
Hi Tommy,
Thank you so much for answering my questions and your explanations.
Best regards,
Reihaneh