Subclass of UIViewController (mimics UITableViewController) is not freed

I have subclassed a UIViewController that mimics a UITableViewController == HUDTableViewController . Then I subclass from this controller subclass ( SomeViewController : HUDTableViewController ).

If I simulate a memory warning, SomeViewController will not be released. Here is the HUDTableViewController code:

 using System; using Foundation; using UIKit; namespace MyApp { public class HUDTableViewController : UIViewController, IUITableViewDataSource, IUITableViewDelegate, IDisposable, IUIScrollViewDelegate { private UIView parentView; private UITableView tableView; public UITableView TableView { get { return this.tableView; } set { this.tableView = value; } } public HUDTableViewController() : base() { Initialize(); } private void Initialize() { this.tableView = new UITableView(); this.tableView.TranslatesAutoresizingMaskIntoConstraints = false; this.tableView.WeakDelegate = this; this.tableView.WeakDataSource = this; this.parentView = new UIView(); } public override void ViewDidLoad() { base.ViewDidLoad(); this.parentView.AddSubview(this.tableView); View = this.parentView; NSMutableDictionary viewsDictionary = new NSMutableDictionary(); viewsDictionary["parent"] = this.parentView; viewsDictionary["tableView"] = this.tableView; this.parentView.AddConstraints(NSLayoutConstraint.FromVisualFormat("H:|[tableView]|", (NSLayoutFormatOptions)0, null, viewsDictionary)); this.parentView.AddConstraints(NSLayoutConstraint.FromVisualFormat("V:|[tableView]|", (NSLayoutFormatOptions)0, null, viewsDictionary)); } [Foundation.Export("numberOfSectionsInTableView:")] public virtual System.nint NumberOfSections(UIKit.UITableView tableView) { return 1; } public virtual System.nint RowsInSection(UIKit.UITableView tableview, System.nint section) { throw new NotImplementedException(); } public virtual UIKit.UITableViewCell GetCell(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath) { throw new NotImplementedException(); } [Export("tableView:estimatedHeightForRowAtIndexPath:")] public virtual System.nfloat EstimatedHeight(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath) { return UITableView.AutomaticDimension; } [Foundation.Export("tableView:didSelectRowAtIndexPath:")] public virtual void RowSelected(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath) { } [Export("tableView:heightForRowAtIndexPath:")] public virtual System.nfloat GetHeightForRow(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath) { return 44.0f; } [Foundation.Export("tableView:heightForHeaderInSection:")] public virtual System.nfloat GetHeightForHeader(UIKit.UITableView tableView, System.nint section) { return UITableView.AutomaticDimension; } [Foundation.Export("tableView:viewForHeaderInSection:")] public virtual UIKit.UIView GetViewForHeader(UIKit.UITableView tableView, System.nint section) { return null; } [Export("tableView:titleForHeaderInSection:")] public virtual string TitleForHeader(UITableView tableView, nint section) { return string.Empty; } [Foundation.Export("tableView:willDisplayCell:forRowAtIndexPath:")] public virtual void WillDisplay(UIKit.UITableView tableView, UIKit.UITableViewCell cell, Foundation.NSIndexPath indexPath) { } } } 

tableView should have a reference count of 2 (due to AddSubView and my property).

This is the main view controller that creates SomeViewController :

 public class MasterViewContainer : UIViewController { private bool hasSetupHandlersAndEvents = false; // ... public override void ViewWillAppear (bool animated) { base.ViewWillAppear (animated); if (!hasSetupHandlersAndEvents) { if (listButton != null) { listButton.Clicked += listButton_Clicked; } hasSetupHandlersAndEvents = true; } } public override void ViewWillDisappear (bool animated) { base.ViewWillDisappear (animated); if (hasSetupHandlersAndEvents) { if (listButton != null) { listButton.Clicked -= listButton_Clicked; } hasSetupHandlersAndEvents = false; } } private void listButton_Clicked(object sender, EventArgs args){ SomeViewController viewController = new SomeViewController(); viewController.SomeEvent += SomeEventHandler; NavigationController.PushViewController(viewController, false); } } 

As you can see, SomeViewController has a link to the MasterViewContainer , due to SomeEventHandler .

SomeViewController freed if I use

 public class SomeViewController : UITableViewController 

but it will not be released if I use

 public class SomeViewController : HUDTableViewController 

The Dispose method is never called. I do not see the reference loop. Where do I need to release something? What am I missing?

Try 1:

This is the only solution that comes to my mind. I use a field (class variable) where I keep a link to SomeViewController . In DidReceiveMemoryWarning I manually release / delete it. When I want to access a field, I check to see if it was previously initialized. If not, I initialize it if necessary.

 public class MasterViewContainer : UIViewController { private SomeViewController viewController; 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. if (this.viewController != null) { this.viewController.SomeEvent -= SomeEventHandler; this.viewController.Dispose(); this.viewController = null; } } private void listButton_Clicked(object sender, EventArgs args){ if (this.viewController == null) { this.viewController = new SomeViewController(); this.viewController.SomeEvent += SomeEventHandler; } NavigationController.PushViewController(this.viewController, false); } 

But this solution is not perfect. Recognition is also called when the view is currently displayed on the screen. Thus, he has very big failures.

Bounty:

I would like to have a solution that explains the problem of memory management. Why is he not being released? What needs to be changed to free him (without doing things, as in my attempt). It should behave like a UITableViewController .

Try 2:

Now I tried to override the Dispose(bool disposing) HUDTableViewController :

 protected override void Dispose(bool disposing) { if(!this.disposed) { if(disposing) { this.tableView.RemoveFromSuperview(); this.tableView.Dispose(); } this.disposed = true; } base.Dispose(disposing); } 

Neither this Dispose method of the HUDTableViewController nor the Dispose SomeViewController method is SomeViewController .

+6
source share
1 answer

Call super if you want your parent view to also call the same function that controls your leadership. Depending on the location, you will not need to do any other manual disposal.

 public override void DidReceiveMemoryWarning () { // If you want the superclass to fire the function first call super first // and vice versa. super.didReceiveMemoryWarning(); // Releases the view if it doesn't have a superview. base.DidReceiveMemoryWarning (); 
0
source

All Articles