【问题标题】:ReactiveCocoa takeUntil 2 possible signals?ReactiveCocoa takeUntil 2 个可能的信号?
【发布时间】:2015-06-27 18:33:36
【问题描述】:

所以我已经成功地将一个按钮变成了一个可以改变标签的开关。

我还能够让它在发生这种情况时启动定时处理设置,并且它能够关闭定时处理。

无论如何,我需要关闭定时过程,我想知道是否有办法在不使用一次性用品的情况下停止它。使用第二个 takeUntil 信号。

编辑我认为我试图做的有点误导,让我展示我当前有效的解决方案。

-(RACSignal*) startTimer {
    return [[RACSignal interval:1.0
            onScheduler:[RACScheduler mainThreadScheduler]]
             startWith:[NSDate date]];
}

-(void) viewWillAppear:(BOOL)animated {}

-(void) viewDidLoad {

    self.tableView.delegate = self;
    self.tableView.dataSource = self;

    RACSignal* pressedStart = [self.start rac_signalForControlEvents:UIControlEventTouchUpInside];

    @weakify(self);
    RACSignal* textChangeSignal = [pressedStart map:^id(id value) {
        @strongify(self);
        return [self.start.titleLabel.text isEqualToString:@"Start"] ? @"Stop" : @"Start";
    }];

    // Changes the title
    [textChangeSignal subscribeNext:^(NSString* text) {
        @strongify(self);
        [self.start setTitle:text forState:UIControlStateNormal];
    }];

    RACSignal* switchSignal = [[textChangeSignal map:^id(NSString* string) {
        return [string isEqualToString:@"Stop"] ? @0 : @1;

    }] filter:^BOOL(id value) {
        NSLog(@"Switch %@",value);
        return [value boolValue];
    }];


    [[self rac_signalForSelector:@selector(viewWillAppear:)]
     subscribeNext:^(id x) {


    }];

    static NSInteger t = 0;
    // Remake's it self once it is on finished.
    self.disposable = [[[textChangeSignal filter:^BOOL(NSString* text) {
        return [text isEqualToString:@"Stop"] ? [@1 boolValue] : [@0 boolValue];
    }]
                                  flattenMap:^RACStream *(id value) {
                                      NSLog(@"Made new Sheduler");
                                      @strongify(self);
                                      return [[self startTimer] takeUntil:switchSignal];
                                  }] subscribeNext:^(id x) {
                                      NSLog(@"%@",x);
                                      @strongify(self);
                                      t = t + 1;
                                      NSLog(@"%zd",t);

                                      [self updateTable];
                                  }];

    [[self rac_signalForSelector:@selector(viewWillDisappear:)] subscribeNext:^(id x) {
        NSLog(@"viewWillAppear Dispose");
        [self.disposable dispose];
    }];

}


-(BOOL) isGroupedExcercisesLeft {
    BOOL isGroupedLeft = NO;
    for (int i =0;i < [self.excercises count]; i++) {
        Excercise* ex = [self.excercises objectAtIndex:i];
        if(ex.complete == NO && ex.grouped == YES) {
            isGroupedLeft = YES;
            break;
        }
    }
    return isGroupedLeft;
}

-(void) updateTable {

    // Find the
    NSInteger nextRow;

    if (([self.excercises count] > 0 || self.excercises !=nil) && [self isGroupedExcercisesLeft]) {

        for (int i =0;i < [self.excercises count]; i++) {

            Excercise* ex = [self.excercises objectAtIndex:i];
            if(ex.complete == NO && ex.grouped == YES) {
                nextRow = i;
                break;
            }

        }

        NSIndexPath* path = [NSIndexPath indexPathForItem:nextRow inSection:0];
        NSArray* indexPath = @[path];
        // update //

        Excercise* ex = [self.excercises objectAtIndex:nextRow];
        [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
        if (ex.seconds <= 0) {
            RLMRealm* db = [RLMRealm defaultRealm];
            [db beginWriteTransaction];
            ex.complete = YES;
            [db commitWriteTransaction];
        }
        else {
            // Update Seconds
            RLMRealm* db = [RLMRealm defaultRealm];
            [db beginWriteTransaction];
            ex.seconds = ex.seconds - 1000;
            NSLog(@"Seconds: %zd",ex.seconds);
            [db commitWriteTransaction];
            // Update table
            [self.tableView reloadRowsAtIndexPaths:indexPath withRowAnimation:UITableViewRowAnimationNone];
        }


    } else {
        NSLog(@"Done");

        SIAlertView *alertView = [[SIAlertView alloc] initWithTitle:@"Deskercise" andMessage:@"Excercises Complete"];
        [alertView addButtonWithTitle:@"Ok"
                                 type:SIAlertViewButtonTypeDefault
                              handler:^(SIAlertView *alert) {

                              }];

        alertView.transitionStyle = SIAlertViewTransitionStyleBounce;
        [alertView show];
        NSLog(@"Dispose");
        [self.disposable dispose];

    }

}

【问题讨论】:

    标签: ios reactive-cocoa


    【解决方案1】:

    使用takeUntil:self.completeSignal 的问题在于,当您将completeSignal 更改为另一个值时,它不会传递给任何已经在等待completeSignal 先前持有的变量的函数。

    - (RACSignal*) startTimer {
        @weakify(self)
        return [[[RACSignal interval:1.0
                     onScheduler:[RACScheduler mainThreadScheduler]]
             startWith:[NSDate date]]
            takeUntil:[[self.start rac_signalForControlEvents:UIControlEventTouchUpInside]
                       merge:[[RACObserve(self, completeSignal) skip:1] flattenMap:
                              ^RACStream *(RACSignal * signal) {
                                  @strongify(self)
                                  return self.completeSignal;
                              }]]
            ];
    }
    

    信号现在正在观察和平坦化completeSignal,这将产生预期的效果。 takeUntil: 会忽略未发送下一个事件而完成的信号,因此请使用self.completedSignal = [RACSignal return:nil],它发送单个下一个事件然后完成。

    但是,这段代码并不理想,让我们看看更好的解决方案。

    @property (nonatomic, readwrite) RACSubject * completeSignal;

    - (RACSignal*) startTimer {
        return [[[RACSignal interval:1.0
                     onScheduler:[RACScheduler mainThreadScheduler]]
             startWith:[NSDate date]]
            takeUntil:[[self.start rac_signalForControlEvents:UIControlEventTouchUpInside]
                       merge:self.completeSignal]
            ];
    }
    - (void) viewDidLoad {
        [super viewDidLoad];
        self.completeSignal = [RACSubject subject];
        self.tableView.delegate = self;
        self.tableView.dataSource = self;
    
        RACSignal * pressedStart = [self.start rac_signalForControlEvents:UIControlEventTouchUpInside];
    
        @weakify(self);
        RACSignal* textChangeSignal = [[pressedStart startWith:nil] scanWithStart:@"Stop" reduce:^id(id running, id next) {
            return @{@"Start":@"Stop", @"Stop":@"Start"}[running];
        }];
    
        [self.start
         rac_liftSelector:@selector(setTitle:forState:)
         withSignals:textChangeSignal, [RACSignal return:@(UIControlStateNormal)], nil];
    
        [[[pressedStart flattenMap:^RACStream *(id value) { //Using take:1 so that it doesn't get into a feedback loop
            @strongify(self);
            return [self startTimer];
        }] scanWithStart:@0 reduce:^id(NSNumber * running, NSNumber * next) {
            return @(running.unsignedIntegerValue + 1);
        }] subscribeNext:^(id x) {
            @strongify(self);
            [self updateTable];
            NSLog(@"%@", x);
        }];
    }
    
    - (void) updateTable {
        //If you uncomment these then it'll cause a feedback loop for the signal that calls updateTable
    
        //[self.start sendActionsForControlEvents:UIControlEventTouchUpInside];
        //[self.completeSignal sendNext:nil];
        if ([self.excercises count] > 0 || self.excercises !=nil) {
        } else {
        }
    }
    

    让我们浏览一下更改列表:

    • completeSignal 现在是 RACSubject(手动控制的 RACSignal)。
    • 为了纯度和摆脱@weakify 指令,textChangeSignal 现在使用方便的scanWithStart:reduce: 方法,它允许您访问累加器(这适用于使用递增或递减数字的方法)。
    • start 的文本现在正在由 rac_liftSelector 函数更改,该函数接受 RACSignals 并在全部触发后解包它们。
    • 您的flattenMap:pressedStart 替换为[self startTimer] 现在使用scanWithStart:reduce,这是一种更实用的计数方式。

    我不确定您是否通过让updateTable 包含完成信号来进行测试,但这肯定会导致您的flattenMap:pressedButton 出现逻辑问题,由此产生的反馈循环最终会在堆栈溢出时使程序崩溃。

    【讨论】:

    • 扫描中有大理石图吗?我不太确定它是如何工作的。
    • rxmarbles.com/#scan,但我也会尝试解释一下。 Scan 具有保留状态的能力,这就是running 变量的含义,它是一个有状态的参数,它为每个块传入并被该块返回的任何内容替换。这对于可以基于先前输出在两个不同输出之间触发的信号(例如 @"Stop" 和 @"Start")非常有用。
    • 所以我猜 scan with start 在处理下一个信号之前首先应用 @0 信号?
    • 没错,subscribeNext: 会看到的第一个变量是@1。
    • 所以我不确定合并将如何阻止自我更新表调度停止你能解释一下它是如何做到的吗?为了明确起见,我希望这个触发器停止 [self updatetable] 函数调用,这是我在编辑的解决方案中使用一次性的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    相关资源
    最近更新 更多