summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Maps.iOS/MapRenderer.cs
diff options
context:
space:
mode:
authorE.Z. Hart <hartez@users.noreply.github.com>2017-01-12 21:43:57 (GMT)
committerJason Smith <jason.smith@xamarin.com>2017-01-12 21:43:57 (GMT)
commitba7b66b83a83328f56e0676efcff5d3c553ac6bf (patch)
tree0d77274282c801aaadd3954850703f21b3967285 /Xamarin.Forms.Maps.iOS/MapRenderer.cs
parentb6cb64e4930de9b16309f2d30c6bb0a2177048fd (diff)
downloadxamarin-forms-ba7b66b83a83328f56e0676efcff5d3c553ac6bf.zip
xamarin-forms-ba7b66b83a83328f56e0676efcff5d3c553ac6bf.tar.gz
xamarin-forms-ba7b66b83a83328f56e0676efcff5d3c553ac6bf.tar.bz2
Fix out-of-memory crashes on iOS when creating maps (#467)
* Pool map views on iOS 10 to avoid memory issues Clean up disposal implementation for map renderer, delegate * Enable 39489 test on iOS * Add missing XF Maps references * Add missing maps reference to Windows 8.1 project * Actually enable the test for iOS * Fix placement of Isolate override * Don't create a new control for ImageRenderer if NewElement is null * Reverting fixes to ViewRenderer Dispose; have to fix that in a separate PR
Diffstat (limited to 'Xamarin.Forms.Maps.iOS/MapRenderer.cs')
-rw-r--r--Xamarin.Forms.Maps.iOS/MapRenderer.cs79
1 files changed, 72 insertions, 7 deletions
diff --git a/Xamarin.Forms.Maps.iOS/MapRenderer.cs b/Xamarin.Forms.Maps.iOS/MapRenderer.cs
index af1f080..cc9f253 100644
--- a/Xamarin.Forms.Maps.iOS/MapRenderer.cs
+++ b/Xamarin.Forms.Maps.iOS/MapRenderer.cs
@@ -17,9 +17,10 @@ namespace Xamarin.Forms.Maps.iOS
internal class MapDelegate : MKMapViewDelegate
{
// keep references alive, removing this will cause a segfault
- static readonly List<object> List = new List<object>();
- readonly Map _map;
+ readonly List<object> List = new List<object>();
+ Map _map;
UIView _lastTouchedView;
+ bool _disposed;
internal MapDelegate(Map map)
{
@@ -103,31 +104,80 @@ namespace Xamarin.Forms.Maps.iOS
targetPin.SendTap();
}
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+
+ if (disposing)
+ {
+ _map = null;
+ _lastTouchedView = null;
+ }
+
+ base.Dispose(disposing);
+ }
}
public class MapRenderer : ViewRenderer
{
CLLocationManager _locationManager;
bool _shouldUpdateRegion;
+ bool _disposed;
+
+ const string MoveMessageName = "MapMoveToRegion";
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
return Control.GetSizeRequest(widthConstraint, heightConstraint);
}
+ // iOS 10 has some issues with releasing memory from map views; each one we create allocates
+ // a bunch of memory we can never get back. Until that's fixed, we'll just reuse MKMapViews
+ // as much as possible to prevent creating new ones and losing more memory
+
+ // For the time being, we don't want ViewRenderer handling disposal of the MKMapView
+ // if we're on iOS 10; during Dispose we'll be putting the MKMapView in a pool instead
+ protected override bool ManageNativeControlLifetime => !FormsMaps.IsiOs10OrNewer;
+
protected override void Dispose(bool disposing)
{
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+
if (disposing)
{
if (Element != null)
{
var mapModel = (Map)Element;
- MessagingCenter.Unsubscribe<Map, MapSpan>(this, "MapMoveToRegion");
+ MessagingCenter.Unsubscribe<Map, MapSpan>(this, MoveMessageName);
((ObservableCollection<Pin>)mapModel.Pins).CollectionChanged -= OnCollectionChanged;
}
var mkMapView = (MKMapView)Control;
mkMapView.RegionChanged -= MkMapViewOnRegionChanged;
+ mkMapView.GetViewForAnnotation = null;
+ mkMapView.Delegate.Dispose();
+ mkMapView.Delegate = null;
+ mkMapView.RemoveFromSuperview();
+
+ if (FormsMaps.IsiOs10OrNewer)
+ {
+ // This renderer is done with the MKMapView; we can put it in the pool
+ // for other rendererers to use in the future
+ MapPool.Add(mkMapView);
+ }
+
+ // For iOS versions < 10, the MKMapView will be disposed in ViewRenderer's Dispose method
if (_locationManager != null)
{
@@ -146,7 +196,7 @@ namespace Xamarin.Forms.Maps.iOS
if (e.OldElement != null)
{
var mapModel = (Map)e.OldElement;
- MessagingCenter.Unsubscribe<Map, MapSpan>(this, "MapMoveToRegion");
+ MessagingCenter.Unsubscribe<Map, MapSpan>(this, MoveMessageName);
((ObservableCollection<Pin>)mapModel.Pins).CollectionChanged -= OnCollectionChanged;
}
@@ -156,14 +206,30 @@ namespace Xamarin.Forms.Maps.iOS
if (Control == null)
{
- SetNativeControl(new MKMapView(RectangleF.Empty));
+ MKMapView mapView = null;
+
+ if (FormsMaps.IsiOs10OrNewer)
+ {
+ // See if we've got an MKMapView available in the pool; if so, use it
+ mapView = MapPool.Get();
+ }
+
+ if (mapView == null)
+ {
+ // If this is iOS 9 or lower, or if there weren't any MKMapViews in the pool,
+ // create a new one
+ mapView = new MKMapView(RectangleF.Empty);
+ }
+
+ SetNativeControl(mapView);
+
var mkMapView = (MKMapView)Control;
var mapDelegate = new MapDelegate(mapModel);
mkMapView.GetViewForAnnotation = mapDelegate.GetViewForAnnotation;
mkMapView.RegionChanged += MkMapViewOnRegionChanged;
}
- MessagingCenter.Subscribe<Map, MapSpan>(this, "MapMoveToRegion", (s, a) => MoveToRegion(a), mapModel);
+ MessagingCenter.Subscribe<Map, MapSpan>(this, MoveMessageName, (s, a) => MoveToRegion(a), mapModel);
if (mapModel.LastMoveToRegion != null)
MoveToRegion(mapModel.LastMoveToRegion, false);
@@ -202,7 +268,6 @@ namespace Xamarin.Forms.Maps.iOS
MoveToRegion(((Map)Element).LastMoveToRegion, false);
_shouldUpdateRegion = false;
}
-
}
void AddPins(IList pins)