summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.iOS
diff options
context:
space:
mode:
authoradrianknight89 <adrianknight89@outlook.com>2017-01-26 14:24:31 (GMT)
committerRui Marinho <me@ruimarinho.net>2017-01-26 14:24:31 (GMT)
commit6b2a69d930d42657aff2b9ad769503b4939568ab (patch)
treee0b98e6ee63c0161a7e9cee69f285661e1685fb5 /Xamarin.Forms.Platform.iOS
parent6670ca58458582a1a670f97d17958220802f84b0 (diff)
downloadxamarin-forms-6b2a69d930d42657aff2b9ad769503b4939568ab.zip
xamarin-forms-6b2a69d930d42657aff2b9ad769503b4939568ab.tar.gz
xamarin-forms-6b2a69d930d42657aff2b9ad769503b4939568ab.tar.bz2
[iOS/Critical] Fix ListView memory leaks (#524)
* fix memory leaks added sample code changed page name Changed page title add screen instructions fix copy paste error change subview dispose logic Fix context action memory leak add sample code change custom page names Revert "change custom page names" This reverts commit 7aaab2625d9526080ca5c51c02e909f09ee3f610. changed class names changes UI test for 32206 update ui test fix whitespace UITests done * run UI tests on iOS only * fix code style * dispose prototype * fix child renderer issue * add null check
Diffstat (limited to 'Xamarin.Forms.Platform.iOS')
-rw-r--r--Xamarin.Forms.Platform.iOS/Cells/CellTableViewCell.cs18
-rw-r--r--Xamarin.Forms.Platform.iOS/Cells/ViewCellRenderer.cs16
-rw-r--r--Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs149
3 files changed, 134 insertions, 49 deletions
diff --git a/Xamarin.Forms.Platform.iOS/Cells/CellTableViewCell.cs b/Xamarin.Forms.Platform.iOS/Cells/CellTableViewCell.cs
index e32e86c..b42f69f 100644
--- a/Xamarin.Forms.Platform.iOS/Cells/CellTableViewCell.cs
+++ b/Xamarin.Forms.Platform.iOS/Cells/CellTableViewCell.cs
@@ -7,8 +7,8 @@ namespace Xamarin.Forms.Platform.iOS
public class CellTableViewCell : UITableViewCell, INativeElementView
{
Cell _cell;
-
public Action<object, PropertyChangedEventArgs> PropertyChanged;
+ bool _disposed;
public CellTableViewCell(UITableViewCellStyle style, string key) : base(style, key)
{
@@ -95,5 +95,21 @@ namespace Xamarin.Forms.Platform.iOS
return nativeCell;
}
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ if (disposing)
+ {
+ PropertyChanged = null;
+ _cell = null;
+ }
+
+ _disposed = true;
+
+ base.Dispose(disposing);
+ }
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Platform.iOS/Cells/ViewCellRenderer.cs b/Xamarin.Forms.Platform.iOS/Cells/ViewCellRenderer.cs
index 8f8a92c..30c1723 100644
--- a/Xamarin.Forms.Platform.iOS/Cells/ViewCellRenderer.cs
+++ b/Xamarin.Forms.Platform.iOS/Cells/ViewCellRenderer.cs
@@ -48,9 +48,12 @@ namespace Xamarin.Forms.Platform.iOS
internal class ViewTableCell : UITableViewCell, INativeElementView
{
WeakReference<IVisualElementRenderer> _rendererRef;
-
ViewCell _viewCell;
+ Element INativeElementView.Element => ViewCell;
+ internal bool SupressSeparator { get; set; }
+ bool _disposed;
+
public ViewTableCell(string key) : base(UITableViewCellStyle.Default, key)
{
}
@@ -66,10 +69,6 @@ namespace Xamarin.Forms.Platform.iOS
}
}
- Element INativeElementView.Element => ViewCell;
-
- internal bool SupressSeparator { get; set; }
-
public override void LayoutSubviews()
{
//This sets the content views frame.
@@ -115,6 +114,9 @@ namespace Xamarin.Forms.Platform.iOS
protected override void Dispose(bool disposing)
{
+ if (_disposed)
+ return;
+
if (disposing)
{
IVisualElementRenderer renderer;
@@ -126,8 +128,12 @@ namespace Xamarin.Forms.Platform.iOS
_rendererRef = null;
}
+
+ _viewCell = null;
}
+ _disposed = true;
+
base.Dispose(disposing);
}
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs
index 3c23580..fd0f15f 100644
--- a/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs
+++ b/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs
@@ -29,6 +29,7 @@ namespace Xamarin.Forms.Platform.iOS
IListViewController Controller => Element;
ITemplatedItemsView<Cell> TemplatedItemsView => Element;
public override UIViewController ViewController => _tableViewController;
+ bool _disposed;
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
@@ -90,28 +91,31 @@ namespace Xamarin.Forms.Platform.iOS
}
}
+ void DisposeSubviews(UIView view)
+ {
+ foreach (UIView subView in view.Subviews)
+ DisposeSubviews(subView);
+
+ view.RemoveFromSuperview();
+ view.Dispose();
+ }
+
protected override void Dispose(bool disposing)
{
- // check inset tracker for null to
- if (disposing && _insetTracker != null)
- {
- _insetTracker.Dispose();
- _insetTracker = null;
+ if (_disposed)
+ return;
- var viewsToLookAt = new Stack<UIView>(Subviews);
- while (viewsToLookAt.Count > 0)
+ if (disposing)
+ {
+ if (_insetTracker != null)
{
- var view = viewsToLookAt.Pop();
- var viewCellRenderer = view as ViewCellRenderer.ViewTableCell;
- if (viewCellRenderer != null)
- viewCellRenderer.Dispose();
- else
- {
- foreach (var child in view.Subviews)
- viewsToLookAt.Push(child);
- }
+ _insetTracker.Dispose();
+ _insetTracker = null;
}
+ foreach (UIView subview in Subviews)
+ DisposeSubviews(subview);
+
if (Element != null)
{
var templatedItems = TemplatedItemsView.TemplatedItems;
@@ -119,27 +123,28 @@ namespace Xamarin.Forms.Platform.iOS
templatedItems.GroupedCollectionChanged -= OnGroupedCollectionChanged;
}
+ if (_dataSource != null)
+ {
+ _dataSource.Dispose();
+ _dataSource = null;
+ }
+
if (_tableViewController != null)
{
_tableViewController.Dispose();
_tableViewController = null;
}
- }
- if (disposing)
- {
if (_headerRenderer != null)
{
- var platform = _headerRenderer.Element.Platform as Platform;
- if (platform != null)
- platform.DisposeModelAndChildrenRenderers(_headerRenderer.Element);
+ var platform = _headerRenderer.Element?.Platform as Platform;
+ platform?.DisposeModelAndChildrenRenderers(_headerRenderer.Element);
_headerRenderer = null;
}
if (_footerRenderer != null)
{
- var platform = _footerRenderer.Element.Platform as Platform;
- if (platform != null)
- platform.DisposeModelAndChildrenRenderers(_footerRenderer.Element);
+ var platform = _footerRenderer.Element?.Platform as Platform;
+ platform?.DisposeModelAndChildrenRenderers(_footerRenderer.Element);
_footerRenderer = null;
}
@@ -154,6 +159,8 @@ namespace Xamarin.Forms.Platform.iOS
Control?.TableFooterView?.Dispose();
}
+ _disposed = true;
+
base.Dispose(disposing);
}
@@ -602,6 +609,7 @@ namespace Xamarin.Forms.Platform.iOS
internal class UnevenListViewDataSource : ListViewDataSource
{
IVisualElementRenderer _prototype;
+ bool _disposed;
public UnevenListViewDataSource(ListView list, FormsUITableViewController uiTableViewController) : base(list, uiTableViewController)
{
@@ -661,27 +669,47 @@ namespace Xamarin.Forms.Platform.iOS
{
var target = viewCell.View;
if (_prototype == null)
- {
_prototype = Platform.CreateRenderer(target);
- Platform.SetRenderer(target, _prototype);
- }
else
- {
_prototype.SetElement(target);
- Platform.SetRenderer(target, _prototype);
- }
+
+ Platform.SetRenderer(target, _prototype);
var req = target.Measure(tableView.Frame.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins);
target.ClearValue(Platform.RendererProperty);
- foreach (var descendant in target.Descendants())
+ foreach (Element descendant in target.Descendants())
+ {
+ IVisualElementRenderer renderer = Platform.GetRenderer(descendant as VisualElement);
descendant.ClearValue(Platform.RendererProperty);
+ renderer?.Dispose();
+ }
return (nfloat)req.Request.Height;
}
+
var renderHeight = cell.RenderHeight;
return renderHeight > 0 ? (nfloat)renderHeight : DefaultRowHeight;
}
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ _disposed = true;
+
+ if (disposing)
+ {
+ if (_prototype != null)
+ {
+ _prototype.Dispose();
+ _prototype = null;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
}
internal class ListViewDataSource : UITableViewSource
@@ -689,14 +717,15 @@ namespace Xamarin.Forms.Platform.iOS
const int DefaultItemTemplateId = 1;
static int s_dataTemplateIncrementer = 2; // lets start at not 0 because
readonly nfloat _defaultSectionHeight;
- readonly Dictionary<DataTemplate, int> _templateToId = new Dictionary<DataTemplate, int>();
- readonly UITableView _uiTableView;
- readonly FormsUITableViewController _uiTableViewController;
- protected readonly ListView List;
+ Dictionary<DataTemplate, int> _templateToId = new Dictionary<DataTemplate, int>();
+ UITableView _uiTableView;
+ FormsUITableViewController _uiTableViewController;
+ protected ListView List;
IListViewController Controller => List;
protected ITemplatedItemsView<Cell> TemplatedItemsView => List;
bool _isDragging;
bool _selectionFromNative;
+ bool _disposed;
public ListViewDataSource(ListViewDataSource source)
{
@@ -1016,6 +1045,29 @@ namespace Xamarin.Forms.Platform.iOS
((INotifyCollectionChanged)templatedList.ShortNames).CollectionChanged -= OnShortNamesCollectionChanged;
}
}
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ if (disposing)
+ {
+ if (List != null)
+ {
+ List.ItemSelected -= OnItemSelected;
+ List = null;
+ }
+
+ _templateToId = null;
+ _uiTableView = null;
+ _uiTableViewController = null;
+ }
+
+ _disposed = true;
+
+ base.Dispose(disposing);
+ }
}
}
@@ -1031,11 +1083,12 @@ namespace Xamarin.Forms.Platform.iOS
internal class FormsUITableViewController : UITableViewController
{
- readonly ListView _list;
+ ListView _list;
IListViewController Controller => _list;
UIRefreshControl _refresh;
bool _refreshAdded;
+ bool _disposed;
public FormsUITableViewController(ListView element)
{
@@ -1117,15 +1170,25 @@ namespace Xamarin.Forms.Platform.iOS
protected override void Dispose(bool disposing)
{
- base.Dispose(disposing);
+ if (_disposed)
+ return;
- if (disposing && _refresh != null)
+ if (disposing)
{
- _refresh.ValueChanged -= OnRefreshingChanged;
- _refresh.EndRefreshing();
- _refresh.Dispose();
- _refresh = null;
+ if (_refresh != null)
+ {
+ _refresh.ValueChanged -= OnRefreshingChanged;
+ _refresh.EndRefreshing();
+ _refresh.Dispose();
+ _refresh = null;
+ }
+
+ _list = null;
}
+
+ _disposed = true;
+
+ base.Dispose(disposing);
}
void CheckContentSize()