ios - UIPageViewController 不会释放最后显示的对话框

标签 ios objective-c xamarin.ios xamarin

我正在展示一个简单的 UIPageViewController 并向其中添加一些非常简单和愚蠢的 subview Controller 。当 UIPageViewController 被解雇时,我正在处理所有 subview Controller ,当前未显示的(在 ChildViewControllers 中列出)和显示的(在 ViewControllers 中列出)。未显示的被释放,显示的不被释放。

我已将此分解为一个简单的失败测试,​​因此我确信这与 subview Controller 的内容或其他一些问题无关。我不知道是什么保留了它。

样本:

大师(介绍)

public class MasterDialog : UIPageViewController
{
    public event EventHandler OnDialogClosed;

    private UIBarButtonItem _backButton;

    public MasterDialog() :  base(
        UIPageViewControllerTransitionStyle.Scroll, 
        UIPageViewControllerNavigationOrientation.Horizontal, 
        UIPageViewControllerSpineLocation.None, 
        25)
    {
        _backButton = new UIBarButtonItem(UIBarButtonSystemItem.Cancel);
        _backButton.Clicked += Close;

        NavigationItem.SetLeftBarButtonItem(_backButton, false);
    }

    public override void ViewDidDisappear(bool animated)
    {
        base.ViewDidDisappear(animated);

        OnDialogClosed(this, EventArgs.Empty);
    }

    private void Close(object sender, EventArgs arguments)
    {
        _backButton.Clicked -= Close;

        NavigationController.DismissViewController(true, null);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);

        Console.WriteLine("Master disposed");
    }
}

主数据源
public class DataSource : UIPageViewControllerDataSource
{
     public override UIViewController GetPreviousViewController(
        UIPageViewController pageViewController, UIViewController referenceViewController)
     {
         var detail = (DetailDialog)referenceViewController;

         if (detail.Page - 1 == 0)
             return null;

         return GetViewController(detail.Page - 1);
     }

     public override UIViewController GetNextViewController(
        UIPageViewController pageViewController, UIViewController referenceViewController)
     {
         var detail = (DetailDialog)referenceViewController;

         return GetViewController(detail.Page + 1);
     }

     public UIViewController GetViewController(int page)
     {
         return new DetailDialog(page);
     }
}

细节( child )
public class DetailDialog : UITableViewController
{
    public int Page { get; private set; }

    public DetailDialog(int page) : base(UITableViewStyle.Plain)
    {
        Page = page;
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        Console.WriteLine("Detail init: " + Page + " / " + GetHashCode());

        var label = new UILabel();
        label.Text = "#" + Page;
        label.ContentMode = UIViewContentMode.Center;
        label.Frame = new System.Drawing.RectangleF(0, 100, 320, 50);
        label.BackgroundColor = UIColor.Green;

        Add(label);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);

        Console.WriteLine("Detail disposed: " + Page + " / " + GetHashCode());
    }
}

打开对话框(起点)
    public class StartDialog : UIViewController
    {
        private DataSource _dataSource;
        private MasterDialog _master;

        public StartDialog()
        {
            Title = "WTF";
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            var button = new UIButton(UIButtonType.Custom);
            button.SetTitle("Open", UIControlState.Normal);
            button.BackgroundColor = UIColor.Green;
            button.Frame = new System.Drawing.RectangleF(20, 150, 280, 44);

            Add(button);

            button.TouchDown += OpenMasterDialog;
        }

        private void OpenMasterDialog(object sender, EventArgs arguments)
        {
            _dataSource = new DataSource();

            _master = new MasterDialog();
            _master.DataSource = _dataSource;
            _master.OnDialogClosed += HandleOnDialogClosed;

            _master.SetViewControllers(
                new [] { _dataSource.GetViewController(1) }, 
                UIPageViewControllerNavigationDirection.Forward, 
                false, 
                null
            );

            NavigationController.PresentViewController(
                new UINavigationController(_master), 
                true, 
                null
            );
        }

        private void HandleOnDialogClosed(object sender, EventArgs e)
        {
            _dataSource.Dispose();
            _dataSource = null;

            Console.WriteLine("Before: " + _master.ChildViewControllers.Length +
                "/" + _master.ViewControllers.Length + ")");

            var childs = _master
                .ChildViewControllers.ToList()
                    .Union(_master.ViewControllers);

            foreach (UIViewController child in childs)
            {
                child.RemoveFromParentViewController();
                child.Dispose();
            }

            Console.WriteLine("After: " + _master.ChildViewControllers.Length + 
                "/" + _master.ViewControllers.Length + ")");

            _master.OnDialogClosed -= HandleOnDialogClosed;
            _master.Dispose();
            _master = null;
        }
    }

最佳答案

我可能误解了您的代码/意图,但在这种情况下,在我看来一切都很好。无论如何,这是我的发现......

Detail disposed: 1 / 36217954
After: 0/1)

第 2 行显示 /1我认为这是问题所在。这是正常的,因为您是 回复 - 显示 View Controller ,IOW 代码:
_master.ViewControllers.Length

调用viewControllers UIPageViewController 上的选择器.返回:“页面 View Controller 显示的 View Controller 。”仍然是 DetailDialog那时(即使 master 不再显示)。

这不是 Xamarin 特定的,ObjC 应用程序将在同一时间点返回相同的( native )实例。

这已经解释了——但它仍然没有被释放,为什么?

new Dispose semantics Dispose 之后保留托管对象,只要 native 端需要它(但没有 native 引用,因此它可以被 native 释放,随后在托管端释放)。

在这种情况下, 的生命周期原生 对象还没有结束(即 iOS 仍然引用它),所以它在托管端仍然存在。
        _master.Dispose();
        _master = null;

这将删除 托管 引用 _master但同样(与上面相同)它不会被释放(也不会是 DetailDialog )只要 native _master使用实例(带有 native 引用)。

那么谁得到了 _master 的引用?
      NavigationController.PresentViewController(
            new UINavigationController(_master), 

^ 这将创建一个 UINavigationController只要它还活着,就会有对其他人的引用。

当我处理 UINavigationController (我将它保存在一个字段中)然后 Master* 和 Detail* 实例从 HeapShot 中消失。
    _nav.Dispose();
    _nav = null;

关于ios - UIPageViewController 不会释放最后显示的对话框,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21147136/

相关文章:

ios - 如何以编程方式设置 CFBundleVersion(和/或)CFBundleShortVersionString?

ios - 以编程方式更改 UITableView 的框架

c# - NSArray 的、原始类型和拳击哦,我的天哪!

ios - 解析 XML 数据 objective-c

iphone - Monotouch 记录选项

mono - 无效的 IL 代码 IL_0038 : newobj in MonoTouch

ios - Xcode 6 beta 2 问题导出 .ipa : "Your account already has a valid iOS distribution certificate"

ios - 从 Objective-C 的角度来看 "over ride"是什么意思?

c# - AppDelegate 已经定义了 FinishedLaunching

ios - ZBAR 无法扫描 PDF-417 iOS