Monotouch UITableviewCells从未被销毁过

时间:2013-03-20 09:38:10

标签: memory-management uitableview memory-leaks xamarin.ios

我有一个名为WaitTableView的UITableView控制器。它只有一个单元格,这里是UITableViewCell类的代码:

    public class TableViewWaitCell : UITableViewCell
    {
        public UIActivityIndicatorView activityIndicator = new UIActivityIndicatorView(UIActivityIndicatorViewStyle.Gray);
        public UILabel lblLoading = new UILabel();

        public TableViewWaitCell(UITableViewCellStyle style, string reuseIdentifier) : base (style, reuseIdentifier)
        {
            this.SelectionStyle = UITableViewCellSelectionStyle.None;
        }

        ~TableViewWaitCell(){
            System.Console.WriteLine("TableViewWaitCell.~TableViewWaitCell");
            lblLoading = null;
            activityIndicator = null;   
            System.GC.Collect();
        }
       protected override void Dispose (bool disposing){
            System.Console.WriteLine("TableViewWaitCell.Dispose");
            lblLoading = null;
            activityIndicator = null;
            base.Dispose (disposing);
            GC.Collect();
        }

        public override void Draw (System.Drawing.RectangleF rect)
        {
            base.Draw (rect);

            var context = UIGraphics.GetCurrentContext();
            var gradient = new CGGradient(
            CGColorSpace.CreateDeviceRGB(),
                new float[] { 1f, 1f, 1f, 1f,
                              0.68f, 0.68f, 0.72f, 1f },
                new float[] { 0f, 1f } );
                context.DrawLinearGradient(gradient,
                    new PointF(rect.X+rect.Width/2, rect.Y),
                    new PointF(rect.X+rect.Width/2, rect.Y+rect.Height),
                    CGGradientDrawingOptions.DrawsAfterEndLocation);

            var activityIndicatorViewFrame = new RectangleF(rect.X + rect.Width/2-10, rect.Y+10, 20, 20);
            this.activityIndicator  .Frame = activityIndicatorViewFrame;
            this.activityIndicator.AutoresizingMask = UIViewAutoresizing.FlexibleDimensions;
            this.activityIndicator.StartAnimating();
            this.AddSubview(this.activityIndicator);

            var labelFrame = new RectangleF(rect.X, rect.Y+10+activityIndicatorViewFrame.Height, rect.Width, 35);
            this.lblLoading.Frame = labelFrame;
            this.lblLoading.AutoresizingMask = UIViewAutoresizing.FlexibleDimensions;
            this.lblLoading.TextColor = UIColor.Black;
            this.lblLoading.BackgroundColor = UIColor.Clear;
            this.lblLoading.TextAlignment = UITextAlignment.Center;
            this.lblLoading.Text = Dictionary.GetValue("Loading");
            this.AddSubview(this.lblLoading);
        }
    }

这是主ViewWillDisappear的{​​{1}}方法:

UIViewController

我的问题是析构函数和我的单元格的Dispose都没有被调用。当我运行快照时,TableViewWaitCell类的实例数量会增长,我可以浏览我的应用程序。我不明白Monotouch如何管理细胞生命周期,我可能做错了什么?

2 个答案:

答案 0 :(得分:2)

我没有看到任何会导致您共享的代码中出现此问题的内容。但是,您不会显示单元格的构造和存储方式。您的对象可能会被您未在示例代码中显示的根保持活动状态。我在下面创建了一个示例,显示了Dispose和被调用的终结器。

使用模拟器快速收集表格视图和单元格。通常在按下“显示表”之后。按钮第二次。

在设备上运行会显示不同的行为。表格和单元格不会立即收集。最简单的解释是GC已被调整'只在需要时运行。因此,如果您的应用程序没有使用太多内存,则所有对象将继续存在。

有两件事可以强制GC运行,如示例所示。

showTable.TouchUpInside += delegate {
    navController.PushViewController (new MyViewController (), true);
    // Not a great idea to call Collect but you could to force it.
    // GC.Collect ();
};

allocate.TouchUpInside += delegate {
    // Trigger the GC by creating a bunch of objects
    System.Collections.Generic.List<object> list = new System.Collections.Generic.List <object> ();
    for (int i=0; i<2048; i++)
    {
        list.Add (new object ());
    }
};

首先你可以调用GC.Collect。但是我不建议这样做。当你想让它运行时,GC会运行得最好。 (在大多数情况下。)When is it acceptable to call GC.Collect?

其次只是继续编写代码并让GC决定什么是最好的。在示例中,我添加了另一个按钮,用于分配一堆对象并将它们添加到列表中。因此,如果您在表格视图和主视图之间切换几次,然后按几次分配按钮,您应该看到终结器运行。

using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.CoreGraphics;
using System.Drawing;

namespace delete20130320
{
    [Register ("AppDelegate")]
    public partial class AppDelegate : UIApplicationDelegate
    {
        UIWindow window;


        public override bool FinishedLaunching (UIApplication app, NSDictionary options)
        {
            window = new UIWindow (UIScreen.MainScreen.Bounds);

            var mainView = new UIViewController ();
            var showTable = UIButton.FromType (UIButtonType.RoundedRect);
            showTable.Frame = new System.Drawing.RectangleF (10, 10, 150, 35);
            showTable.SetTitle ("Show Table", UIControlState.Normal);

            var allocate = UIButton.FromType (UIButtonType.RoundedRect);
            allocate.Frame = new System.Drawing.RectangleF (10, 55, 150, 35);
            allocate.SetTitle ("Allocate", UIControlState.Normal);

            mainView.View.BackgroundColor = UIColor.White;
            mainView.View.Add (showTable);
            mainView.View.Add (allocate);

            var navController = new UINavigationController (mainView);

            showTable.TouchUpInside += delegate {
                navController.PushViewController (new MyViewController (), true);
                // Not a great idea to call Collect but you could to force it.
                // GC.Collect ();
            };

            allocate.TouchUpInside += delegate {
                // Trigger the GC by creating a bunch of objects
                System.Collections.Generic.List<object> list = new System.Collections.Generic.List <object> ();
                for (int i=0; i<2048; i++)
                {
                    list.Add (new object ());
                }
            };


            window.RootViewController = navController;

            window.MakeKeyAndVisible ();

            return true;
        }
    }

    public class MyViewController : UIViewController
    {
        UITableView _tableView;
        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();
            _tableView = new UITableView (this.View.Bounds);
            View.Add (_tableView);

            _tableView.DataSource = new MyDataSource ();
        }

        ~MyViewController ()
        {
            // Bad practice to call other managed objects in finalizer
            // But for sample purposes it will be ok
            Console.WriteLine ("~MyViewController");
        }

        protected override void Dispose (bool disposing)
        {
            // Bad practice to call other managed objects in Dispose
            // But for sample purposes it will be ok
            Console.WriteLine ("MyViewController.Dispose");
            base.Dispose (disposing);
        }

        class MyDataSource : UITableViewDataSource
        {
            public override int RowsInSection (UITableView tableView, int section)
            {
                return 1;
            }

            public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
            {
                var cell = tableView.DequeueReusableCell ("SomeUniqueString");
                if (cell != null)
                    return cell;

                return new TableViewWaitCell (UITableViewCellStyle.Default, "SomeUniqueString");
            }
        }
    }

    public class TableViewWaitCell : UITableViewCell
    {
        public TableViewWaitCell(UITableViewCellStyle style, string reuseIdentifier) : base (style, reuseIdentifier)
        {
            this.SelectionStyle = UITableViewCellSelectionStyle.None;
            this.TextLabel.Text = "Something";
        }

        ~TableViewWaitCell()
        {
            // Bad practice to call other managed objects in finalizer
            // But for sample purposes it will be ok
            System.Console.WriteLine("TableViewWaitCell.~TableViewWaitCell"); 
            // Avoid forcing the GC
            //System.GC.Collect();
        }
        protected override void Dispose (bool disposing)
        {
            // Bad practice to call other managed objects in Dispose
            // But for sample purposes it will be ok
            System.Console.WriteLine("TableViewWaitCell.Dispose");
            base.Dispose (disposing);
            //GC.Collect();
        }
    }
}

答案 1 :(得分:0)

问题来自于一个EventHandler,它引用了我的视图控制器的方法,从而预防了我的单元格和控制器的收集。