Если вы сомневаетесь, проверьте RACSignal+Operations.h и RACStream.h, , потому что обязан быть оператором за то, что вы хотите сделать. В этом случае основным недостающим элементом является -scanWithStart:reduce:.
Прежде всего, давайте рассмотрим baseSignal
. Логика будет оставаться в основном те же, за исключением того, что мы должны публиковать a connection для него:
RACMulticastConnection *timer = [[[RACSignal
interval:0.05 onScheduler:[RACScheduler mainThreadScheduler]]
take:percentRemaining]
publish];
Это так, что мы можем использовать один таймер между всеми зависимыми сигналов. Несмотря на то, что вы также предоставили baseSignal
, то будет воссоздать таймер для каждого абонента (включая зависимые сигналы), который может привести к крошечным отклонениям в их запуске.
Теперь мы можем использовать -scanWithStart:reduce:
для увеличения countLabel
и уменьшает progressView
. Этот оператор принимает предыдущие результаты и текущее значение и позволяет нам преобразовывать или комбинировать их, но мы хотим.
В нашем случае, однако, мы просто хотим, чтобы пропустить текущее значение (NSDate
послал по +interval:
), поэтому мы можем только манипулировать предыдущий:
RAC(self.countLabel, text) = [[[timer.signal
scanWithStart:@0 reduce:^(NSNumber *previous, id _) {
return @(previous.unsignedIntegerValue + 1);
}]
startWith:@0]
map:^(NSNumber *count) {
return count.stringValue;
}];
RAC(self.progressView, progress) = [[[timer.signal
scanWithStart:@(percentRemaining) reduce:^(NSNumber *previous, id _) {
return @(previous.unsignedIntegerValue - 1);
}]
startWith:@(percentRemaining)]
map:^(NSNumber *percent) {
return @(percent.unsignedIntegerValue/100.0);
}];
The -startWith:
операторы в выше, кажутся излишними, но это необходимо для обеспечения того, чтобы text
и progress
были установлены до timer.signal
имеет отправлено что угодно.
Затем мы будем использовать обычную подписку для завершения.Это совершенно возможно, что эти побочные эффекты могут быть превращены в сигналы, а также, но это трудно знать, не видя код:
[timer.signal subscribeCompleted:^{
// Move along...
}];
Наконец, поскольку мы использовали RACMulticastConnection
выше, ничего не будет на самом деле огонь еще. Соединения должны быть запущены вручную:
[timer connect];
Это соединяет все из вышеуказанных подписок, и стартует таймер, поэтому значения начинают течь к свойствам.
Теперь это, очевидно, больше кода, чем императивного эквивалент, поэтому можно спросить, почему это стоит. Есть несколько преимуществ:
- Расчеты стоимости теперь поточно-, потому что они не зависят от стороны эффектов. Если вам нужно реализовать что-то более дорогое, то очень просто переместить важную работу в фоновый поток.
- Аналогично вычисляются значения независимых друг друга. Они могут быть легко распараллелены, если это когда-либо станет ценным.
- Вся логика в настоящее время местная привязка. Вам не нужно задаваться вопросом: , где происходят изменения или беспокоиться о заказе (например, между инициализацией и обновлением), потому что все это в одном месте и может быть прочитано сверху вниз.
- Значения могут быть рассчитаны без ссылки на изображение. В случае пример, в Model-View-ViewModel, , подсчет и прогресс будут фактически определены в view model, , а затем уровень представления - это всего лишь набор немых привязок.
- Изменение значений потока от только один вход. Если вам вдруг понадобится , введите другой источник входного сигнала (например, реальный прогресс вместо таймера), есть только одно место, которое вам нужно изменить.
В принципе, это классический пример императивного и функционального программирования.
Хотя императивный код может начинаться с меньшей сложности, он растет по сложности экспоненциально. Функциональный код (и особенно функциональный реактивный код) может начать сложнее, но тогда его сложность возрастает linearly - это намного проще управлять, поскольку приложение растет.
Человек, ты зверь! Жутко хорошо! Этот пост будет стоить значка «Звездный ответ», если бы мы были более крупным сообществом. – allprog
@allprog Спасибо! :) –
Что сказал @allprog! Вау. Спасибо - действительно не мог попросить лучшего объяснения. –