2013-02-23 3 views
1

Я пытаюсь структурировать свое приложение с использованием шаблона MVVM. Поэтому у меня есть ViewModels, которые поднимают события при изменении данных, и ожидается, что пользовательский интерфейс отреагирует на эти события и обновит видимые элементы пользовательского интерфейса.Обнаружение, когда UITableViewCells полностью завершено

У меня есть производный UITableViewCell, который инициализируется определенной ViewModel каждый раз, когда новая ячейка создается или удаляется (очень похожа на miguel's example here). Одним из основных отличий, являющимся частью инициализации, является подписка на событие ViewModel. Это создает ссылку из долгоживущего ViewModel на эту конкретную ячейку, удерживая ее в памяти на всю жизнь ViewModel. Когда ячейка повторно используется, старая подписка очищается и создается новая, созданная для новой модели ViewModel, которая работает нормально.

Однако проблема заключается в том, что нет возможности очистить последнюю подписку после того, как ячейка полностью закончена, что означает, что она хранится в памяти на протяжении всей жизни ViewModel (гораздо дольше, чем я хочу быть). «Полностью закончена» зависит от иерархии VC, но в этом случае полученный DialogViewController, содержащий TableView с пользовательскими ячейками, был выбит из стека UINavigationController и был удален.

willMoveToSuperview никогда не был вызван (я надеялся, что это будет с переданным «null»). removeFromSuperview никогда не называется. Утилизация на каждую ячейку никогда не вызывается. Утилизация UITableViewController не уничтожает каждую ячейку. Устранение TableView внутри контроллера даже не уничтожает каждую ячейку.

Единственный способ, которым я могу вручную распоряжаться каждой ячейкой (и, следовательно, очищать подписки), - это перечисление ячеек вручную самостоятельно в каждом из моих производных UIViewControllers, чего я хочу избежать.

Есть ли у кого-либо подобные проблемы, подобные этому? Я не могу первыми использовать шаблон MVVM с UITableViewCells. Является ли это ошибкой с шаблоном Dispose в базовых оболочках MonoTouch UIKit?

EDIT: Вот вырезанная версия одного из изготовленных на заказ UITableViewCells. Примечание. Я принимаю прагматичный подход, когда я явно подписываюсь на события свойств, которые, как я знаю, могут меняться, а не полный MVVM, связывают каждое свойство с пользовательским интерфейсом. Так что мой обязательный код состоит из только стандартных подписок на события:

public class MyCustomCell : UITableViewCell 
{ 
    private InvoiceViewModel currentViewModel; 

    private readonly UILabel label1; 
    private readonly UILabel label2; 

    public MyCustomCell(NSString reuseId) 
     : base(UITableViewCellStyle.Default, reuseId) 
    { 
     Accessory = UITableViewCellAccessory.DisclosureIndicator; 
     SelectedBackgroundView = new UIView() 
     { 
      BackgroundColor = UIColor.FromRGB(235,235,235), 
     }; 

     label1 = new UILabel(); 
     ContentView.Add(label1); 
     // The rest of the UI setup... 
    } 

    public void Update(MyViewModel viewModel) 
    { 
     if (currentViewModel == viewModel) 
      return; 

     if (currentViewModel != null) 
     { 
      // Cleanup old bindings. 
      currentViewModel.UnacknowledgedRemindersChanged -= HandleNotificationsChanged; 
     } 

     currentViewModel = viewModel; 

     if (viewModel != null) 
     { 
      viewModel.UnacknowledgedRemindersChanged += HandleNotificationsChanged; 

      label1.Text = viewModel.SomeProperty; 
      // Update the rest of the UI with the current view model. 
     } 
    } 

    private void HandleNotificationsChanged() 
    { 
     // Event can fire on background thread. 
     BeginInvokeOnMainThread(() => 
     { 
      // Relevant UI updates go here. 
     }); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     // Unsubscribes from ViewModel events. 
     Update(null); 
     base.Dispose(disposing); 
    } 
} 

И мой производный класс MT.D элемент имеет 1: 1 элемент: ViewModel, поэтому метод GetCell выглядит следующим образом:

public override UITableViewCell GetCell (UITableView tv) 
    { 
     var cell = (MyCustomCell) tv.DequeueReusableCell(key); 
     if (cell == null) 
      cell = new MyCustomCell(key); 

     cell.Update(viewModel); 
     return cell; 
    } 

ответ

1

Вы определенно не первый, кто делает Mvvm-таблицы с MonoTouch.

Я уже писал об этом недавно в http://slodge.blogspot.co.uk/2013/01/uitableviewcell-using-xib-editor.html

До этого были проекты в НДЦ (поиск «Полетам Норвегия») и был запущен проект давно MVVM построен на вершине MonoTouch.Dialog.


В MvvmCross приложениях мы используем таблицы, связанные с ObservableCollections и к другим классам IList много.

Внутри них мы обычно не сталкиваемся с множеством проблем с левосторонними ссылками, но это потому, что мы обычно не призываем людей использовать долгоживущие ViewModels - мы пытаемся создать новый экземпляр данных ViewModel, чтобы каждый вид. Однако я понимаю, что это не подходит для всех приложений.


Когда пользователь MVX находит себя с такого рода проблемы, то некоторые из подходов, мы пытались это:

  • в iOS5 мы действительно использовали метод ViewDidUnload убирать привязок - но очевидно, что теперь ушел в iOS6.
  • В зависимости от стиля представления пользовательского интерфейса (модальный, splitview, navigationcontroller, popup и т. Д.) Мы попытались вручную определить, когда «выскочил», и с помощью этого, чтобы очистить привязки
  • снова в зависимости от стиля презентации пользовательского интерфейса, мы попытались использовать событие ViewDidAppear, ViewDidDisappear добавлять и убирать привязки
  • для всех классов UIKit с привязкой данных мы всегда использовали Dispose в качестве дополнительным места, чтобы попытаться очистить привязки
  • мы смотрели на используя WeakReferences (особенно от ViewModel до View), чтобы обойти проблемы, в которых объекты UIKit принадлежат как iOS/ObjC, так и MonoTouch/.Net - эти проблемы особенно сложны для отладки.
  • Этот слабый ссылочный код, вероятно, будет одним из ключевых изменений в следующей версии.

Пример дискуссии по этому поводу (в Droid, а не в Touch) находится на https://github.com/slodge/MvvmCross/issues/17


Я сожалею, что не могу предложить вам какие-либо конкретные рекомендации в данный момент. Если вы разместите еще один пример кода о том, как вы создаете и сохраняете свои привязки, я мог бы помочь больше, но я не могу представить, какие привязки вы создаете прямо сейчас.

У меня есть еще несколько ссылок. Я добавлю к этому ответу позже - на мобильном телефоне в настоящее время - слишком сложно добавить их сюда!


Update - еще какое-то объяснение на идеях WeakReference, это то, где мы идем сейчас в v3 для MvvmCross - https://github.com/slodge/MvvmCross/tree/vNextDialog/Cirrious/Cirrious.MvvmCross.Binding/WeakSubscription - в основном идея заключается в том, чтобы использовать одноразовые подписки WeakReference событий - которые не будут сохраняйте объекты UIKit в ОЗУ. Это еще не проверенный код. Когда это будет, тогда я буду вести блог и говорить об этом более полно!

+0

Спасибо Stuart! Я отредактировал свой вопрос, чтобы включить некоторый пример кода. Я думаю, что сейчас я собираюсь использовать более строгий подход к жизненному циклу ViewModel. Что касается вашего слабого подхода к мероприятиям делегатов: действуйте с осторожностью. Мы сделали это некоторое время назад, и это дополнительное усложнение, которое все разработчики должны держать в своих головах. например Подписка на эти события с помощью лямбда-подписки с закрытием очень затрудняет отслеживание ошибок, так как ничто не содержит экземпляр закрывающей копии в памяти и поэтому в какой-то момент в будущем будет GCed, эффективно отменяя ваш обработчик событий. – Tyson

+0

Спасибо @Tyson - Я недавно был там с lambdas в реализации Messenger :) Внимание! (Вот почему эта тема была открыта 8 месяцев) – Stuart

+0

Просто добавьте перекрестную ссылку на другие материалы WeakReference http://stackoverflow.com/ а/14734264/373321 – Stuart

Смежные вопросы