2014-01-15 18 views
1

我正在呈現一個簡單的UIPageViewController並向其中添加一些非常簡單且愚蠢的子視圖控制器。當UIPageViewController被解散時,我將處理所有子視圖控制器,即當前未顯示的子視圖控制器(在ChildViewControllers中列出)和顯示的控件(在ViewControllers中列出)。未顯示的人得到釋放,顯示的人得不到。UIPageViewController不會釋放最後顯示的對話框

我已經把它分解爲一個簡單的失敗測試,​​所以我確定它不是關於子視圖控制器的內容或其他一些問題。我不知道是什麼保留它。

樣品:

碩士(提交)

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

詳細(兒童)

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; 
     } 
    } 
+0

出於好奇,你嘗試過'sgen'和'sgen' +'newrefcount'進行垃圾回收嗎?你也有這個在repo/.zip的地方嗎?我今晚可能會有一些*免費*時間來檢查這個(並滿足我好奇心的另一部分)。 – poupou

+0

嗨@poupou,我沒有嘗試任何比垃圾收集的默認配置,因爲我正在努力防止內存泄漏數週,不想面對任何愚蠢的副作用,因爲實驗性的東西(如果你會說它是安全的使用,我雖然會試試看)。你可以在這裏下載這個項目:https://www.dropbox.com/s/9y22lsw2fityr2r/Xamarin.iOS.UIPageViewControllerMemory.zip 讓我知道你是否可以找到一分鐘來看看它,如果它是一個錯誤或只是我錯了:-)。 –

回答

3

我可能會誤解你的代碼/意圖,但在這種情況下,在我看來,一切都是幾乎罰款。反正這是我的發現......

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

2號線顯示/1我以爲是問題。這是正常的,因爲你是重新 -surfacing視圖控制器,督察代碼:

_master.ViewControllers.Length 

呼籲UIPageViewControllerviewControllers選擇。這將返回:「頁面視圖控制器顯示的視圖控制器。」「仍然是DetailDialog(即使不再顯示master)。

這不是Xamarin特有的,ObjC應用程序會在同一時間點返回相同的(本機)實例。

這是解釋 - 但它仍然沒有釋放後,爲什麼?

根據new Dispose semantics管理對象被保持,Dispose後,只要本機側需要它的(但沒有本機參考,以便可以釋放本身,並隨後釋放在託管側)。

在這種情況下天然對象的生命週期尚未結束(即iOS的仍具有參照它),所以它仍保持在被管理側活着

 _master.Dispose(); 
     _master = null; 

這消除了管理引用_master但又(同上),它不會是釋放(既不會DetailDialog)只要本地_master實例使用(與本地引用)。

那麼誰參考了_master

 NavigationController.PresentViewController(
      new UINavigationController(_master), 

^創建一個UINavigationController只要它是活着的也有別人引用。

當我處置UINavigationController(我把它放在一個字段中)時,Master *和Detail *實例從HeapShot中消失。

_nav.Dispose(); 
    _nav = null; 
+0

感謝您的努力!我意識到GC與RC的情況,這就是爲什麼我正在重建我的整個應用程序以釋放所有打開的東西。在這一點上,我沒有擺脫最後一個細節對話框(你的假設是正確的),而且移除導航控制器也是合乎邏輯的。但我沒有得到它的工作,處理我的HandleOnDialogClosed導航控制器不會改變任何東西。你可以發佈你的修改過的方法,所以我可以用它來測試嗎?謝謝。 –

+0

當然https://gist.github.com/spouliot/72fd254a1d1223b0a5a0如果你用HeapShot打開/關閉,你將看不到DetailDialog(打開/關閉後)。您可以使用過濾器(拍攝後)進行確認。如果您取消註釋這兩行(Dispose/null),則HeapShot將報告它。 – poupou

+0

好吧,我只是更新到7.0.6,現在那個討厭的DetailDialog已經消失了。之前:沒辦法。不幸的是,我不記得之前運行的是什麼版本。最後一個問題:你爲什麼把導航按鈕放在私人領域?對GC有用嗎? –