Reactivecocoa: Повторяющийся сигнал с переменным интервалом

Созданный на 1 апр. 2015  ·  6Комментарии  ·  Источник: ReactiveCocoa/ReactiveCocoa

У меня есть 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 , так что я думаю, что тогда я должен сделать то же самое?

question

Самый полезный комментарий

- (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];
        }];
}

Все 6 Комментарий

Если таймер никогда не предназначен для завершения, вы можете создать рекурсивный метод, который запускает 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];
        }];
}

Закрытие из-за бездействия.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги