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 SomeViewController
Dispose
nazywa.
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. –
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
Nie powinieneś zdecydowanie implementować metody Dispose()? –