Avoiding common memory pitfalls

Memory on mobile devices is certainly not an unlimited commodity. Because of this, memory usage in your application can be much more important than on desktop applications. At times, you might find the need to use a memory profiler, or improve your code to use memory more efficiently.

The following are the most common memory pitfalls:

  • The garbage collector (GC) is unable to collect large objects fast enough to keep up with your application
  • Your code inadvertently causes a memory leak
  • A C# object is garbage collected but is later attempted to be used by native code.

Let's take a look at the first problem where the GC cannot keep up. Let's say, we have a Xamarin.iOS application with a button for sharing an image on Twitter as follows:

twitterShare.TouchUpInside += (sender, e) =>
{
  var image = UImage.FromFile("YourLargeImage.png");
  //Share to Twitter
};

Now let's assume the image is a 10 MB image from the user's camera roll. If the user clicks on the button and cancels the Twitter post rapidly, there could be the possibility of your application running out of memory. iOS will commonly force close apps using too much memory, and you don't want users to experience this with your app.

The best solution is to call Dispose on the image when you are finished with it as follows:

var image = UImage.FromFile("YourLargeImage.png");
//Share to Twitter
image.Dispose();

An even better approach would be to take advantage of the C# using statement as follows:

using(var image = UImage.FromFile("YourLargeImage.png"))
{
  //Share to Twitter
}

The C# using statement will automatically call Dispose in a try-finally block, so the object will get disposed even if an exception is thrown. I recommend taking advantage of the using statement for any IDisposable class where possible. It is not always necessary for small objects such as NSString, but is always a good idea for larger, more heavyweight UIKit objects.

Tip

A similar situation can occur on Android with the Bitmap class. Although slightly different, it is best to call both the Dispose and Recycle methods on this class along with using the BitmapFactory.Options settings for InPurgeable and InInputShareable.

Memory leaks are the next potential issues. C# being a managed, garbage-collected language prevents a lot of memory leaks, but not all of them. The most common leaks in C# are caused by events.

Let's assume we have a static class with an event as follows:

static class MyStatic
{
  public static event EventHandler MyEvent;
}

Now, let's say we need to subscribe to the event from an iOS controller as follows:

public override void ViewDidLoad()
{
  base.ViewDidLoad();

  MyStatic.MyEvent += (sender, e) =>
  {
    //Do something
  };
}

The problem here is that the static class will hold a reference to the controller until the event is unsubscribed. This is a situation that a lot of developers might miss. To fix this issue on iOS, I would subscribe to the event in ViewWillAppear and unsubscribe ViewWillDisappear. On Android, use OnStart and OnStop, or OnPause and OnResume.

You would correctly implement this event as follows:

public override void ViewWillAppear()
{
  base.ViewWillAppear();
  MyStatic.MyEvent += OnMyEvent;
}

public override void ViewWillDisappear()
{
  base.ViewWillDisappear ();
  MyStatic.MyEvent += OnMyEvent;
}

However, an event is not a surefire cause of a memory leak. Subscribing to the TouchUpInside event on a button inside the ViewDidLoad method, for example, is just fine. Since the button lives in memory just as long as the controller, everything can get garbage collected without problem.

For the final issue, the garbage collector can sometimes remove a C# object; later, an Objective-C object attempts to access it.

The following is an example of adding a button to UITableViewCell:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell("MyCell");
  //Remaining cell setup here

  var button = UIButton.FromType(UIButtonType.InfoDark);
  button.TouchUpInside += (sender, e) =>
  {
    //Do something
  };
  cell.AccessoryView = button;
  return cell;
}

We add the built-in info button as an accessory view to the cell. The problem here is that the button will get garbage collected, but its Objective-C counterpart will remain in use as it is displayed on the screen. If you click on the button after a period of time, you will get a crash that looks something like the following:

mono-rt: Stacktrace:
mono-rt:   at <unknown>
mono-rt:   at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) 
mono-rt:   at MonoTouch.UIKit.UIApplication.Main (string[],string,string)  
... Continued ...
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries 
used by your application.
================================================================

It is not the most descriptive error message, but in general, you know that something went wrong in the native Objective-C code. To resolve the issue, create a custom subclass of UITableViewCell and create a dedicated member variable for the button as follows:

public class MyCell : UITableViewCell
{
  UIButton button;
  public MyCell()
  {
    button = UIButton.FromType(UIButtonType.InfoDark);
    button.TouchUpInside += (sender, e) => 
    {
      //Do something
    };
    AccessoryView = button;
  }
}

Now, your GetCell implementation will look something like the following:

public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
  var cell = tableView.DequeueReusableCell("MyCell") as MyCell;
  //Remaining cell setup here

  return cell;
}

Since the button is not a local variable, it will no longer get garbage collected very soon. A crash is avoided, and in some ways this code is a bit cleaner. Similar situations can happen on Android with the interaction between C# and Java; however, it is less likely since both are garbage-collected languages.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.178.207