У меня есть PoC класса, использующего повторяющийся сигнал с переменным интервалом:
// Repeat.h
#import <Foundation/Foundation.h>
<strong i="6">@interface</strong> Repeat : NSObject
- (instancetype)initWithInterval:(NSInteger)interval;
<strong i="7">@property</strong> (assign, nonatomic) NSInteger interval;
<strong i="8">@property</strong> (readonly, nonatomic) RACSubject *dataSubject;
<strong i="9">@end</strong>
// Repeat.m
#import <ReactiveCocoa/ReactiveCocoa.h>
#import "Repeat.h"
<strong i="12">@interface</strong> Repeat ()
<strong i="13">@property</strong> (readwrite, strong, nonatomic) RACSubject *dataSubject;
<strong i="14">@property</strong> (readwrite, strong, nonatomic) RACSignal *dataSignal;
<strong i="15">@end</strong>
<strong i="16">@implementation</strong> Repeat
- (instancetype)initWithInterval:(NSInteger)interval
{
self = [super init];
if (self) {
self.dataSubject = [RACSubject subject];
self.dataSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
static int i = 0;
[subscriber sendNext:@(i++)];
return nil;
}];
self.interval = interval;
[RACObserve(self, interval) subscribeNext:^(id x) {
RACSignal *limit = [RACObserve(self, interval) skip:1];
RACSignal *repeater = [[RACSignal interval:[x integerValue]
onScheduler:[RACScheduler mainThreadScheduler]]
takeUntil:limit];
RACSignal *tickSignal = [repeater doCompleted:^{
NSLog(@"interval changed to: %lu", self.interval);
}];
[[tickSignal flattenMap:^RACStream *(id value) { // *
return self.dataSignal;
}] subscribeNext:^(id x) {
[self.dataSubject sendNext:x];
}];
}];
}
return self;
}
<strong i="17">@end</strong>
Использование будет следующим:
Repeat *repeater = [[Repeat alloc] initWithInterval:3];
[repeater.dataSubject subscribeNext:^(id x) {
// do something with x
}];
...
// somehwere else in the code
repeater.interval = 5;
Это позволяет мне сохранить подписку и изменить только интервал доставки значений сигналов. Меня беспокоит то, что потребитель должен фактически подписаться на тему, а не на сигнал. Я понимаю, что это не распространенный сценарий, и на самом деле правила рекомендуют избегать тем, если это возможно. Я попытался выставить dataSignal
в общедоступном интерфейсе и заменить часть, помеченную // *
следующим образом:
[tickSignal subscribeNext:^(id x) {
[self.dataSignal replayLast];
}];
Но это вообще не сработает, и будет отправлено только начальное значение. Кто-нибудь еще пытался реализовать то, что я пытаюсь достичь здесь? Я уверен, что RAC способен на это, а мой PoC - просто гадкий утенок, которого можно значительно улучшить.
Суть можно найти здесь: https://gist.github.com/eimantas/51d9dfc81136f340a829
Обновление: на основе ответа http://stackoverflow.com/questions/15075075/when-to-use-racreplaysubject-vs-racmulticastconnection
Кажется, что RACMulticastConnection делает то же самое - выставляет RACSubject
как RACSignal
, так что я думаю, что тогда я должен сделать то же самое?
Если таймер никогда не предназначен для завершения, вы можете создать рекурсивный метод, который запускает concat:[RACSignal delay:N]
и после этого вызывает себя из concat:[RACSignal defer:^{}]
.
Другими словами, вставьте задержки в N секунд между бесконечной серией вашего собственного сигнала.
Таймер может быть остановлен по команде пользователя. А что касается рекурсии - мой CS может быть ржавым, но не приведет ли это в конечном итоге к переполнению кадра стека?
@eimantas Нет, потому что задержка не будет синхронной. RAC выйдет из кадра и вернется к коду позже, по истечении задержки.
Извините, но я все еще не понимаю вашего представления (в частности, как мне называть concat:[RACSignal delay:N]
?
- (RACSignal *)doStuffRepeatedly {
return [[[RACSignal
defer:^{
// Do some stuff here
return [RACSignal empty];
}]
// Wait 5 seconds
concat:[RACSignal delay:5]]
then:^{
// Do more stuff
return [self doStuffRepeatedly];
}];
}
Закрытие из-за бездействия.
Самый полезный комментарий