2017-01-11 3 views
0

Я искал простой способ автоматического обновления пользовательского интерфейса при обновлении переменной. Регулярный KVO - это очень грязный код, поэтому я смотрел RxSwift, ReactiveCocoa и т. Д., Но обнаружил, что их трудно понять, и много новых объектов и слишком много вещей, которые мне не нужны.Неправильная ли практика вызова необязательной функции внутри hasSet переменной?

Я играл с переменным didSet вместе с дополнительной опцией emtpy в моем объекте. Надеюсь, это проще показать, чем объяснить:

Пользовательская объект (ViewModel)

class AwesomeViewModel{ 
    var awesomeText:String { didSet { updateBlock?() } } 
    var updateBlock:(()->())? 

    init(awesomeText:String){ 
     self.awesomeText = awesomeText 
    } 
} 

И пользовательские UIView:

class AwesomeView:UIView{ 
    @IBOutlet weak var awesomeLabel: UILabel! 

    func bindViewModel(viewModel:AwesomeViewModel){ 
     viewModel.updateBlock = { [weak self] in 
      self?.awesomeLabel.text = viewModel.awesomeText 
     } 
     viewModel.updateBlock?() 
    } 
} 

Давайте говорить об этом настраиваемое представление AwesomeView существует в UIViewController как выход. Создаю экземпляр AwesomeViewModel, а затем позвоните self.awesomeView.bindViewModel(awesomeViewModel).

Когда я позже, в моем UIViewController (или где-нибудь еще, если на то пошло, он может быть передан вокруг) сделать что-то вроде awesomeViewModel.awesomeText = "Hello World", то didSet переменной в ViewModel будет инициировать дополнительную функцию updateBlock. Поскольку эта функция задана пользовательским представлением AwesomeView, она автоматически обновит текст в метке в этом представлении.

Я думал, что это было довольно круто, и легко понять, но есть ли какие-либо побочные эффекты этого или что-то я не вижу? Это плохая практика? Это кажется намного проще, чем при использовании стандартного КВО со всеми функциями и наблюдения и т.д ..

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

Я надеялся получить некоторые плюсы и минусы по этому методу, прежде чем применять его только к своему приложению чтобы узнать, что это самая глупая идея в мире. Но для меня это нормально.

+2

Самый большой недостаток по сравнению с KVO заключается в том, что он не является зернистым (что может быть хорошо для ваших нужд). Если ваша модель имеет несколько свойств и один «updateBlock», вы никогда не знаете, что было фактически обновлено, только что-то было обновлено. – rmaddy

ответ

2

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

Я хотел бы изменить updateBlock иметь «наблюдаемое» значение в качестве параметра, т.е.

var updateBlock: ((String) ->())? 

Как это может помочь избежать сохранения циклов, и, возможно, переименовать updateBlock более точно отражать , которые значение изменилось , т. е.

var awesomeTextUpdate: ((String) ->())? 

Однако, @rmaddy прав, это становится болью с несколькими свойствами, которые вы, возможно, захотите наблюдать. Это может быть частично разрешено с перечислением свойств и их значений, например.

class AwesomeViewModel { 
    enum Property { 
    case awesomeText(String) 
    case awesomeNumber(Int) 
    } 
} 

и изменения updateBlock иметь тип

var updateBlock: ((AwesomeViewModel.Property) ->())? 

и тому didSet к

var awesomeText: String { 
    didSet { 
    updateBlock?(.awesomeText(awesomeText)) 
    } 
} 

Тогда клиенты могут просто switch на Property перечислении; перерыть, где им все равно, и получить доступ к ним. Это может стать болью, если у вас много потенциально наблюдаемых свойств, но клиенту требуется только один в конкретном случае.

Если кажется, что нет правильного компромисса, это в основном потому, что нет, и RxSwift может быть лучшим долгосрочным решением. Однако, если вы не будете широко тиражировать эту парадигму, факультативная функция прекрасна. Помните: не повторяйте себя.

tl; dr то, что вы делаете, выглядит разумно, но не масштабируется хорошо.

+0

Отличный ответ! Спасибо. Да, пункт @rmaddy действительно ударил меня. Пользовательский интерфейс обновил бы три ненужных раза, если бы я обновил четыре переменные, это неприемлемо. Теперь я думаю о сохранении дополнительной переменной во всех ViewModels, например, bool с именем «needsUpdate», которая является единственной переменной с 'didSet', которая вызывает updateBlock, если для этого bool установлено значение true. Каждый раз, когда я обновляю переменную, мне нужно будет установить этот bool в 'true', и блок будет вызываться, а внутри блока мне придется установить bool обратно в' false'. Возможно, возникнут ненужные обновления пользовательского интерфейса, но только один раз для каждого обновления. – Sti