ReactiveCocoa采取两个可能的信号?

所以我已经成功地将一个button变成了一个closures和开启的标签。

我也能够在发生时启动一个定时的处理过程,并且有能力closures定时过程。

无论如何,我需要的方式来closures定时过程,我想知道是否有一种方法来阻止它,而不使用一次性。 第二个takeUntil信号。

编辑我觉得我想要做的是有点误导让我展示我的当前解决scheme的工作。

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

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

 - (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] ,它发送一个下一个事件,然后完成。

但是,这个代码是非常理想的,让我们看看更好的解决scheme。

@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:方法,它可以让你访问一个累加器(这适用于使用递增或递减数字的方法)。
  • 现在正在通过rac_liftSelector函数更改start的文本, rac_liftSelector函数在RACSignals被打开时解开它们。
  • 你的flattenMap:[self startTimer]replacepressedStart现在使用scanWithStart:reduce ,这是一个更有效的方法来保持计数。

我不确定你是否通过updateTable包含完成信号进行testing,但是它肯定会导致你的flattenMap: pressedButton的逻辑问题,当堆栈溢出时,最终的反馈循环最终会使程序崩溃。