Ответ 1
Я провел пару дней в исходном коде MTD и в профилировщике. Хотя я все еще ищу общие рекомендации относительно того, какой лучший шаблон проектирования для реализации DidReceiveMemoryWarning и ViewDidUnload, у меня есть общие замечания, которые могут быть полезны кому-то:
- MonoTouch.Dialog очень хорошо себя ведет. Это не утечка каких-либо ресурсов при обычном использовании. Он держит дерево управления под DVC.Root и каждый метод Dispose Element Dispose содержит базовый элемент управления UIKit. Вам даже не нужно беспокоиться об утилизации старого RootElement, если вы заменили DVC.Root - средство настройки свойств автоматически использует его для вас. В целом, MTD, похоже, не страдает от существенных проблем с памятью. Существует одно исключение - см. Ниже.
- При создании собственных пользовательских элементов (например, MultilineEntryElement) обязательно переопределите метод Dispose (bool), удалив базовый элемент управления UIKit (например, UITextView) и связав метод базового класса Dispose(). Исходный код проекта Miguel MT.D github содержит множество хороших примеров. Все элементы реализуют стандартный шаблон Dispose (хотя они опускают деструктор/финализатор, который вызывает Dispose (false)).
- При реализации настраиваемых контроллеров представлений обычно нет необходимости реализовывать Dispose в подклассах UIViewController, а также в классах TableView DataSource или Delegate. Когда контроллер просмотра получит GC'ed, он будет правильно вызывать Dispose по его ссылкам. Все ячейки, которые вы выделяете в DataSource, будут правильно настроены.
- В качестве исключения для (3) - я столкнулся с неприятной проблемой при добавлении моего собственного поднабора в ячейку TableView. Этот подвью - это элемент управления, который я создал под названием "UICheckbox", который в конечном итоге наследует UIImageView, который имеет два UIImages (вкл. И выкл.) И публичное событие с именем Clicked. Я испытываю только проблему, когда обработчик события, который ссылается на элементы DataSource, подключен к этому событию (если обработчик события не ссылается на сам DataSource или контроллер, все хорошо). Однако, когда выполняются вышеприведенные условия, и контроллер отклоняется, по-видимому, существует некоторый цикл, который GC не может понять, и каждый UICheckbox, который я помещал в TableView, просочился (вместе с его изображениями). Единственный способ, с помощью которого я работал, это добавить код в ViewDidDisappear, чтобы избавиться от ViewController и очистить его IFF состояния, он больше не находится в стеке навигации. Он взломан, но работает.
-
В общем, я придерживаюсь следующего шаблона для размещения объектов в контроллерах моего представления:
- ничего не выделять в конструкторе (используйте его только для передачи состояния)
- создайте дерево управления в ViewDidLoad (и удалите его в ViewDidUnload). подумайте "InitializeComponent" в XAML (если это поможет). Если UIViewController собирается нажимать DialogViewController на стек навигатора, ViewDidLoad является хорошим местом для создания DVC.
- инициализировать значения в дереве управления в ViewDidAppear. Например. вы можете добавлять/удалять/заменять элементы, разделы и даже корень DVC в этом методе. Но не создавайте новый DVC.
-
Существует общая проблема с утечкой ViewControllers, когда пользователь переводит навигационный стек (я ссылаюсь на ссылку bugzilla в "Обновить" в вопросе). Это также влияет на MT.D. Существует довольно простой способ обхода - добавьте следующую строку кода в ViewDidAppear родительского контроллера представления:
// HACK: touch the ViewControllers array to refresh it (in case the user popped the nav stack) // this is to work around a bug in monotouch (https://bugzilla.xamarin.com/show_bug.cgi?id=1889) // where the UINavigationController leaks UIViewControllers when the user pops the nav stack int count = this.NavigationController.ViewControllers.Length;
Рольф отлично справляется с объяснением, почему эта ошибка возникает и почему обходной путь работает в ссылке bugzilla, поэтому я не буду повторять ее.
Я надеюсь, что кто-то найдет это полезным. Я также надеюсь, что кто-то умнее меня имеет некоторые рекомендации относительно того, как обращаться с DidReceiveMemoryWarning и как разделить работу между этим методом и ViewDidUnload.
Обновление: Еще несколько заметок:
- Теперь я понимаю протокол для DidReceiveMemoryWarning и ViewDidUnload: первый всегда доставляется к каждому контроллеру представления, в то время как последний отправляется только для контроллеров представления, которые в настоящее время не отображаются, И не глубже, чем корень навигации стек. В конце концов, я решил игнорировать DidReceiveMemoryWarning, потому что на самом деле у меня нет изображений, которые я кэширую и могу сбросить (согласно руководству iOS). В ViewDidUnload я освобождаю все ресурсы, которые я выделил в ViewDidLoad.
-
В моем приложении есть TabBar, где на каждой вкладке находится UINavigationController, большая часть которого вызывает DialogViewController. Одной из проблем, с которыми я столкнулся, является утечка DialogViewController после того, как ViewDidUnload отпустил ссылку на него. Я попробовал Disposing DVC в ViewDidUnload, но iOS продолжал настаивать на его повторении, и я получал исключение для вызова селектора на объект GC. Я обнаружил причину - контроллер навигации держался на DVC в массиве ViewControllers. Решение состоит в том, чтобы освободить массив, создав вместо него массив нулевой длины - в ViewDidUnload:
this.ViewControllers = new UIViewController[0];
Старый массив теперь будет GC'ed, так же как и DVC, потому что больше ничего не указывает на него. И iOS никогда не будет повторно запускать объект. Примечание - нет необходимости вызывать Dispose на DVC.