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 SomeViewController
Dispose
viene chiamato.
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. –
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
Non dovresti implementare esplicitamente il metodo Dispose()? –