2016-01-26 12 views
6

Ho sottoclasse UIViewController, che imita UITableViewController == HUDTableViewController. Quindi sottoclassi da questo controller di visualizzazione sottoclasse (SomeViewController : HUDTableViewController).Sottoclasse UIViewController (imita UITableViewController) non rilasciata

Se si simula un avviso di memoria, SomeViewController non viene rilasciato. Ecco il codice di 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 dovrebbe avere un conteggio di riferimento di 2 (a causa della AddSubView e la mia proprietà).

Questo è il principale controller della vista, che crea un'istanza 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); 
    } 
} 

Come si può vedere SomeViewController ha un riferimento a MasterViewContainer, a causa di SomeEventHandler.

SomeViewController è rilasciato se uso

public class SomeViewController : UITableViewController 

, ma non è rilasciato se uso

public class SomeViewController : HUDTableViewController 

Il metodo Dispose non viene mai chiamato. Non vedo un ciclo di riferimento. Dove devo rilasciare qualcosa? Cosa mi manca?

Prova 1:

questa è l'unica soluzione, che mi viene in mente. Io uso un campo (variabile di classe) dove tengo il riferimento a SomeViewController. In DidReceiveMemoryWarning lo sblocco/lo scarico manualmente. Quando voglio accedere al campo, controllo se è stato inizializzato prima. In caso contrario, inizializzarlo quando necessario.

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

Ma questa soluzione non è perfetta. Lo smaltimento è anche chiamato quando la vista è attualmente sullo schermo. Quindi è molto probabile che abbia malfunzionamenti.

Bounty:

Mi piacerebbe avere una soluzione, il che spiega il problema di gestione della memoria. Perché non viene rilasciato? Cosa deve cambiare per essere rilasciato (senza fare cose come nel mio tentativo). Dovrebbe comportarsi come UITableViewController.

Prova 2:

Ora ho cercato di ignorare il Dispose(bool disposing) di HUDTableViewController:

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

Né questo metodo Dispose di HUDTableViewController né il metodo di SomeViewControllerDispose viene chiamato.

+0

Per cosa è necessario 'parentView'? C'è già una vista root per il controller, che è garantita per essere creata in 'ViewDidLoad'. Quindi, invece di aggiungere tableView come sua sottoview, si * sostituisce * con il tuo 'parentView'. La vista originale potrebbe persistere nella gerarchia e fare riferimento al controller, in modo che quest'ultimo non venga rilasciato. –

+0

Io uso questo 'HUDTableViewController' perché voglio centrare uno spinner di caricamento su di esso. Quindi potrei usare questa classe senza ulteriori sforzi. Per il centraggio ho introdotto il 'parentView', perché' View' (che è 'UITableView') non funziona e ho avuto problemi se ho provato a usare il genitore di' UITableView'. Ho alcune opzioni che rilasciano il riferimento in qualche modo? O forse hai un'idea migliore centrando una vista in un 'UITableView'. – testing

+0

Non dovresti implementare esplicitamente il metodo Dispose()? –

risposta

0

Chiama super se vuoi che anche la tua vista genitore chiami la stessa funzione gestisca la tua gestione da lì. A seconda della disposizione, non è necessario alcun altro smaltimento manuale.

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