Reactivecocoa: Signal répété à intervalle variable

Créé le 1 avr. 2015  ·  6Commentaires  ·  Source: ReactiveCocoa/ReactiveCocoa

J'ai un PoC d'une classe utilisant un signal répété avec un intervalle variable:

// 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>

L'utilisation serait la suivante:

Repeat *repeater = [[Repeat alloc] initWithInterval:3];
[repeater.dataSubject subscribeNext:^(id x) {
    // do something with x
}];
...
// somehwere else in the code
repeater.interval = 5;

Cela me permet de conserver l'abonnement et de ne modifier que l'intervalle de livraison des valeurs de signal. Je m'inquiète de la partie que le consommateur doit réellement souscrire au sujet plutôt qu'au signal. Je comprends que ce n'est pas un scénario courant et que les directives encouragent en fait à éviter les sujets si possible. J'ai essayé d'exposer dataSignal dans l'interface publique et de remplacer la partie marquée par // * comme suit:

[tickSignal subscribeNext:^(id x) {
    [self.dataSignal replayLast];
}];

Mais cela ne fonctionnerait pas du tout et seule la valeur initiale serait envoyée. Quelqu'un d'autre a-t-il essayé de mettre en œuvre ce que j'essaie de réaliser ici? Je suis convaincu que RAC est capable de le faire et mon PoC n'est qu'un vilain petit canard qui peut être grandement amélioré.

L'essentiel peut être trouvé ici: https://gist.github.com/eimantas/51d9dfc81136f340a829

Mise à jour: basée sur la réponse de @jspahrsummers à la question sur stackoverflow: http://stackoverflow.com/questions/15075075/when-to-use-racreplaysubject-vs-racmulticastconnection

Il semble que RACMulticastConnection fasse la même chose - exposer RACSubject comme RACSignal , donc je suppose que je devrais faire la même chose alors?

question

Commentaire le plus utile

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

Tous les 6 commentaires

Si le minuteur n'est jamais vraiment censé se terminer, vous pouvez créer une méthode récursive qui exécute concat:[RACSignal delay:N] et s'invoque à partir de concat:[RACSignal defer:^{}] après cela.

En d'autres termes, insérez des délais de N secondes entre une série infinie de votre propre signal.

La minuterie peut être interrompue sur commande de l'utilisateur. Et en ce qui concerne la récursivité - mon CS peut être rouillé, mais cela ne créerait-il pas finalement le débordement du cadre de la pile?

@eimantas Non, car le délai ne sera pas synchrone. RAC quittera le cadre et reviendra au code plus tard une fois le délai écoulé.

Je suis désolé, mais je ne comprends toujours pas votre idée (en particulier - sur quoi dois-je appeler 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];
        }];
}

Fermeture en raison de l'inactivité.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

RuiAAPeres picture RuiAAPeres  ·  3Commentaires

v-silin picture v-silin  ·  4Commentaires

sprynmr picture sprynmr  ·  3Commentaires

tunidev picture tunidev  ·  3Commentaires

lxian picture lxian  ·  6Commentaires