UIViewController 子类(模仿 UITableViewController)没有被释放

Posted

技术标签:

【中文标题】UIViewController 子类(模仿 UITableViewController)没有被释放【英文标题】:UIViewController subclass (mimics UITableViewController) doesn't get released 【发布时间】:2016-05-03 07:46:23 【问题描述】:

我对@9​​87654322@ 进行了子类化,它模仿UITableViewController == HUDTableViewController。然后我从这个子类视图控制器 (SomeViewController : HUDTableViewController) 继承。

如果我模拟内存警告,SomeViewController 不会被释放。这是HUDTableViewController的代码:

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 的引用计数应为 2(因为 AddSubView 和我的财产)。

这是主视图控制器,它实例化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);
    

如您所见,SomeViewController 引用了MasterViewContainer,因为SomeEventHandler

SomeViewController 如果我使用就会被释放

public class SomeViewController : UITableViewController

,但如果我使用它不会发布

public class SomeViewController : HUDTableViewController

永远不会调用Dispose 方法。我没有看到参考周期。我必须在哪里发布一些东西?我错过了什么?

尝试 1:

这是我想到的唯一解决方案。我使用一个字段(类变量)来保存对SomeViewController 的引用。在DidReceiveMemoryWarning 我手动释放/处置它。当我想访问该字段时,我会检查它之前是否已初始化。如果没有,我会在需要时对其进行初始化。

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

但这个解决方案并不完美。当视图当前在屏幕上时,也会调用 dispose。所以很有可能出现故障。

赏金:

我想要一个解决方案,它可以解释内存管理问题。为什么不发布?必须改变什么才能发布它(不做我尝试的事情)。它的行为应该类似于 UITableViewController

尝试 2:

现在我尝试覆盖 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);

HUDTableViewControllerDispose 方法和 SomeViewControllerDispose 方法都没有被调用。

【问题讨论】:

您需要parentView 做什么?控制器已经有一个根视图,保证在ViewDidLoad 中创建。因此,不要将 tableView 添加为其子视图,而是将其 替换parentView。原始视图可能在层次结构中持续存在并引用控制器,因此后者不会被释放。 我使用这个HUDTableViewController 因为我想在它上面放置一个加载微调器。所以我可以毫不费力地使用这个类。对于居中,我引入了parentView,因为View(即UITableView)不起作用,如果我尝试使用UITableView 的父级,我会遇到问题。我是否有一些选择以某种方式发布参考?或者,也许您有一个更好的主意,将视图集中在 UITableView 你不应该明确地实现 Dispose() 方法吗? @FredM:如果我有东西要处理,我可以。问题是我应该处置/释放什么?同样在我的项目中,Dispose() 永远不会自动调用。所以我只能手动拨打Dispose()。默认UITableViewController不需要手动调用Dispose() 你可以试试这个:lostechies.com/chrispatterson/2012/11/29/idisposable-done-right 【参考方案1】:

如果您希望父视图也调用相同的函数来处理您的管理,请调用 super。根据安排,您不需要进行任何其他手动处理。

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

【讨论】:

以上是关于UIViewController 子类(模仿 UITableViewController)没有被释放的主要内容,如果未能解决你的问题,请参考以下文章

UIViewController 子类调用方法

无法从子类更新 UI

UIViewController 的 Swift 子类化子类

为啥 UIViewController 是 UIResponder 的子类?

将 UITableViewController 子类重构为 UIViewController 子类

如何继承 UIViewController 的子类