Thomas Bandt

Xamarin.iOS Memory Pitfalls

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 Monday, 13 April 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.

The Root Cause

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.

Rule #1: Become Paranoid

  • Read as much as you can find about how to handle these kinds of issues before you start to write a single line of your app.
  • Don’t trust code samples you find within the Xamarin docs.
  • Don’t trust code samples you find somewhere else.
  • Don’t trust components from third parties.
  • Don’t even trust your coworker ;-).

Recommendation: Profile, Profile, Profile

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.

Coding Recommendations

1. Avoid Strong References

There are many strategies to avoid strong references, but sometimes these references are even hard to recognize.

What's working:

  1. You can pass a NSObject if you're storing it as WeakReference<> or setting it to null when cleaning up afterwards.
  2. You can pass an Action<> callback, if you're setting it to null when cleaning up afterwards.
  3. An even better way is to use C# event handlers, but don't forget to detach them afterwards.
  4. Best way in terms of loose coupling may be messaging (NSNotificationCenter or Mvx Messenger in MvvmCross), even when it obviously comes with some drawbacks in regards of maintainability.

2. Always Detach Event Handlers and Dispose Observers

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).

3. Clean Up After Yourself

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.)

Conclusion

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.

What do you think? Drop me a line and let me know!