2016-01-26 16 views
6

Mam podklasę UIViewController, która naśladuje UITableViewController == HUDTableViewController. Następnie podklasę z tego podklasowanego kontrolera widoku (SomeViewController : HUDTableViewController).Podklasa UIViewController (naśladuje UITableViewController) nie zostaje zwolniona.

Jeśli zasymuluję ostrzeżenie o pamięci, SomeViewController nie zostanie zwolniony. Oto kod z 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 powinien mieć numer referencyjny 2 (z powodu AddSubView i moją własność).

To jest główny kontroler widoku, który konkretyzuje 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); 
    } 
} 

Jak widać SomeViewController ma odniesienie do MasterViewContainer powodu SomeEventHandler.

SomeViewController jest zwolniony, jeśli mogę użyć

public class SomeViewController : UITableViewController 

, ale to nie jest zwolniony, jeśli mogę użyć

public class SomeViewController : HUDTableViewController 

Sposób Dispose nigdy nie jest wywoływana. Nie widzę cyklu odniesienia. Gdzie muszę coś wydać? Czego mi brakuje?

Spróbuj 1:

Jest to jedyne rozwiązanie, które przychodzi mi do głowy. Używam pola (class variable), w którym przechowuję odniesienie do SomeViewController. W DidReceiveMemoryWarning ręcznie zwalniam/usuwam. Kiedy chcę uzyskać dostęp do pola, sprawdzam, czy zostało ono wcześniej zainicjalizowane. Jeśli nie, zainicjuję go w razie potrzeby.

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

Ale to rozwiązanie nie jest idealne. Zrzut jest również wywoływany, gdy widok jest aktualnie na ekranie. Jest więc bardzo prawdopodobne, że wystąpią usterki.

Bounty:

Chciałbym mieć rozwiązanie, w którym wyjaśniono kwestię zarządzania pamięcią. Dlaczego nie zostaje wydany? Co musi się zmienić, aby to wydać (bez robienia rzeczy jak w mojej próbie). Powinno zachowywać się jak UITableViewController.

Spróbuj 2:

Teraz Próbowałem przesłonić Dispose(bool disposing) z HUDTableViewController:

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

Ani Dispose metoda HUDTableViewController ani metoda SomeViewControllerDispose nazywa.

+0

Do czego potrzebny jest 'parentView'? Istnieje już widok główny dla kontrolera, który jest gwarantowany do utworzenia w 'ViewDidLoad'. Więc zamiast dodawać tableView jako jego podgląd, * zastępujesz * go swoim 'parentView'. Pierwotny widok może utrzymywać się w hierarchii i odwoływać się do kontrolera, więc ten ostatni nie jest zwalniany. –

+0

Używam tego 'HUDTableViewController', ponieważ chcę wyśrodkować na nim spinner ładujący. Więc mógłbym użyć tej klasy bez większego wysiłku. Do centrowania wprowadziłem 'parentView', ponieważ' Widok' (który jest 'UITableView') nie działa i miałem problemy, gdy próbowałem użyć rodzica' UITableView'. Czy mam jakieś opcje jakoś uwolnić odniesienie? A może masz lepszy pomysł wyśrodkowywania widoku w "UITableView". – testing

+0

Nie powinieneś zdecydowanie implementować metody Dispose()? –

Odpowiedz

0

Zadzwoń do Super, jeśli chcesz, aby Twój widok macierzysty również wywoływał tę samą funkcję. W zależności od aranżacji nie trzeba wykonywać żadnych innych ręcznych czynności.

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(); 
Powiązane problemy