One of the huge benefits of using Xamarin for developing cross-platform applications is clearly C#. But that often leads to bad coding practices which can put your app in jeopardy.
Published on Mon, April 13, 2015
To be accurate: It’s not the language C# itself. It's habits most developers adopt while programming .NET applications in environments where memory normally doesn’t matter because resources seem endless, be it on modern web or even desktop applications.
Even most of Xamarin's examples and code snippets are obviously lacking a deeper understanding of the issue.
First of all, even the latest smartphones and tablets cannot be compared to desktop computers, servers, or laptops regarding available RAM. That being said, each app running on iOS can only deal with a very limited amount of memory. And that limit can often be reached very fast.
If you think of a list of images you want to display, each of these UIImage objects may be only a few bytes large, at least from the point of view of the Garbage Collector. But one of these bytes is always a pointer to the underlying "native image", which in fact may be as large as a few megabytes.
Now if it’s not possible for the garbage collector to reach that UIImage or if the collector thinks “Oh hey, that’s only a few bytes, let’s come back later!”, you’re doomed.
That of course is a singularity of Xamarin.iOS, which runs on top of the "native system" and has to deal with memory management from both worlds: Reference Counting on iOS and Garbage Collection on .NET/Mono.
It's a shame that the built-in profiling tools only come with the business edition of Xamarin, because every developer needs the ability to identify memory leaks in her app – all the more when the architecture of the whole tooling is having it's weak spot there.
If you're in the lucky position of having a business edition: go and use the profiling tools exhaustively. If you're not, go with Xcode Instruments instead.
There are many strategies to avoid strong references, but sometimes these references are even hard to recognize.
What's working:
Search your solution for += and make sure you have a -= for each of it. When using NSNotificationCenter, don't just register observers, but also "unregister" them (means: dispose them when no longer needed).
Waiting for the garbage collector to clean up your stuff sometimes is simply not working because references on heavy native resources such as images are not identified as such and therefore deferred.
I came up with a class I call MemoryUtility and an interface I named ICanCleanUpMyself, which both can be found on GitHub.
I make use of it in every single controller I don't need anymore and sometimes even (sub-)views. It's going through the complete hierarchy cleaning up all the things – and some of them with a special treatment.
It's still work in progress yet, but maybe it can be helpful to you, too.
If you're going to use MvvmCross, simply put it in your own presenter before showing another view. If you're not, ViewDidDisappear may be a good option:
public override void ViewDidDisappear(bool animated)
{
if ((NavigationController == null && IsMovingFromParentViewController) || (ParentViewController != null && ParentViewController.IsBeingDismissed))
{
// Start cleaning up
}
base.ViewDidDisappear(animated);
}
(Whenever your controller gets dismissed oder popped, clean up.)
Getting productive with Xamarin.iOS is easy, but by not knowing the pitfalls of that Reference Counting vs. Garbage Collection thing, you are going to create a lot of memory leaks even more easily. When your app grows over time, it will cost you some sleepless nights and maybe days or even weeks to find and fix them afterwards. So it's better to understand what's going on before you start and keep all these things in mind while you're at work.