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);
}
}