Question

I have used Xamarin for a while now, and I have encountered a very strange issue.

I am able to crash a very simple two screens app. On the first screen, I have a UIButton with the TouchUpInside event. On the second, I have a UIImageView with attached image (from local file).

All I have to do is to move back and forward between those two View-Controllers, all the time.

When I attached XCode's Instruments with Activity Monitor on, I noticed that my simple app reaches ~100MB of memory before the memory gets reclaimed, then its usage drops to ~15MB.

But when I loop navigation long enough, memory reaches over 140MB and the app crashes. I have discovered this behaviour while working on a more complex app. Of course, I am taking all available precautions:

  • unsubscribing events on ViewWillDisappear
  • nulling the delegates and so on.

Basicly, in my complex app, I have overriden the Dispose method in my base class for all UIViewControllers, and I can see that the Dispose method is called with disposing == false on every view controller that was displayed. However, the memory usage doesn't drop.

What is wrong with it?

I would like to point out few things:

  • My Xamarin Studio is up to date,
  • Crashes appeared while I was testing applications in debug mode on iPhone 3GS iOS 6.1.3
  • In simple appplication, the image was a 1024x1024 JPG file.

Herewith some code sample:

public partial class SimpleTestViewController : UIViewController 
{
    private UIButton button;
    public SimpleTestViewController () : base ("SimpleTestViewController", null) { }

    public override void DidReceiveMemoryWarning ()
    {
        // Releases the view if it doesn't have a superview.
        base.DidReceiveMemoryWarning ();

        // Release any cached data, images, etc that aren't in use.
    }

    public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();
        button = new UIButton (new RectangleF(0, 100, 100, 50));
        button.BackgroundColor = UIColor.Red;
        button.TouchUpInside += (sender, e) => {
            this.NavigationController.PushViewController(new SecondView(), true);
        };
        this.Add (button);

        // Perform any additional setup after loading the view, typically from a nib.
    }
}

public partial class SecondView : UIViewController
{
    private UIImageView _imageView;

    public SecondView () : base ("SecondView", null) { }

    public override void DidReceiveMemoryWarning ()
    {
        // Releases the view if it doesn't have a superview.
        base.DidReceiveMemoryWarning ();

        // Release any cached data, images, etc that aren't in use.
    }

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

        _imageView = new UIImageView (new RectangleF(0,0, 200, 200));
        _imageView.Image = UIImage.FromFile ("Images/image.jpg");
        this.Add (_imageView);
        // Perform any additional setup after loading the view, typically from a nib.
    }

    protected override void Dispose (bool disposing)
    {
        System.Diagnostics.Debug.WriteLine("Disposing " +this.GetType() 
            + " hash code " + this.GetHashCode() 
            + " disposing flag "+disposing);
        base.Dispose (disposing);
    }
}
Was it helpful?

Solution

You're creating an instance of the button / image and storing them in a backing field, and you're not calling Dispose on them in your controller's Dispose. The Controller instantiated them, so you have to clear them down.

In the example above, you are also not unwiring the buttons TouchUpInside event. I'd suggest not using a lambda for this, and actually create a method for it makes it easier to detach later on.

TouchUpInside -= this.Method; 

In addition, you're not removing the image from the view you added it to.

I know you say you're doing these things in the view, it will dissapear, but that's not happening in your example code. Can you provide a fully worked example with these basics taken care of?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top