RAC(Part2:SideEffect)

作者: 快乐的小问酱 | 来源:发表于2014-11-30 21:58 被阅读928次

函数编程里一切计算都是为了求值,没有副作用是一个显著地特征。从实用角度出发,RAC引入了副作用。

Subject

作为一种“可变”(可变值就是一种副作用,函数�编程里一切值都是不可变的,也就没有变量的概念)的Signal,你可以控制它的值,它就是观察者模式中的Subject。

RACSubject *animals = [RACSubject subject];
[animals subscribeNext:^(id nextObject) {
  NSLog(@"%@", nextObject);
}];
[animals sendNext:@"cat"];

Multicast Connection (publish, multicast, replay)

Signal的副作用一般是在subscribe的时候发生的,并且每次subscribe都会发生,这样很多时候并不是所期望的,例如一个网络操作,被subscribe多次也只执行一次,那么我们就需要将这些订阅连接(connection)起来,也就是多播。

使用publish连接:

RACSignal *signal = [[RACSignal return:@"hello" ] doNext:^ (id nextValue) {
  NSLog(@"nextValue:%@", nextValue);
}];
RACMulticastConnection *connection = [signal publish];
[connection.signal subscribeNext:^(id nextValue) {
  NSLog(@"First %@", nextValue);
}];
[connection.signal subscribeNext:^(id nextValue) {
  NSLog(@"Second %@", nextValue);
}];
[connection connect];

这段代码的输出是:

2014-11-30 15:11:14.711 racdemo[7416:303] nextValue:hello
2014-11-30 15:11:14.712 racdemo[7416:303] First hello
2014-11-30 15:11:14.712 racdemo[7416:303] Second hello

connection connect之后,signal开始发送第一个值,如果connection比subscribe先执行,那么订阅就收不到任何的值。所以可以将信号connect到一个replay subject:

RACMulticastConnection *connection = [signal multicast:[RACReplaySubject subject]];
[connection connect];
[connection.signal subscribeNext:^(id nextValue) {
  NSLog(@"First %@", nextValue);
}];
[connection.signal subscribeNext:^(id nextValue) {
  NSLog(@"Second %@", nextValue);
}];

其实最简单的做法是使用replay:

RACSignal *replaySignal = [signal replay];
[replaySignal subscribeNext:^(id nextValue) {
  NSLog(@"First %@", nextValue);
}];
[replaySignal subscribeNext:^(id nextValue) {
  NSLog(@"Second %@", nextValue);
}];

publish, multicast和replay这几个操作其实都是同一个概念:

- (RACMulticastConnection *)publish {
  RACSubject *subject = [[RACSubject subject] setNameWithFormat:@"[%@] -publish", self.name];
  RACMulticastConnection *connection = [self multicast:subject];
  return connection;
}

- (RACMulticastConnection *)multicast:(RACSubject *)subject {
  [subject setNameWithFormat:@"[%@] -multicast: %@", self.name, subject.name];
  RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject];
  return connection;
}

- (RACSignal *)replay {
  RACReplaySubject *subject = [[RACReplaySubject subject] setNameWithFormat:@"[%@] -replay", self.name];

  RACMulticastConnection *connection = [self multicast:subject];
  [connection connect];

  return connection.signal;
}

这里还有一个cold/hot signal的说法,signal默认是cold的,在每次subscribe的时候才会工作;当一个connection建立之后,这个signal就是hot的,在订阅之前已经处于活动状态。

Command

对于UI组件,例如一个按钮来说,点击的时候要引起副作用。RAC使用了RACCommand�,方便封装这种副作用。

self.ClickMe.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id input) {
  NSLog(@"button pressed");
  return [RACSignal empty];
}];

上面的例子并不能很好的说明RACCommand的本质,一个较为完备的例子:

self.ClickMe.rac_command = [[RACCommand alloc] initWithSignalBlock:^(id input) {
  RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      [subscriber sendNext:@"doing something"];
      [subscriber sendCompleted];
    });
    return nil;
  }];
  return signal;
}];
[self.ClickMe.rac_command.executionSignals subscribeNext:^(id doSomethingSignal) {
  NSLog(@"button pressed, let's do something");
  [doSomethingSignal subscribeCompleted:^{
    NSLog(@"done!");
  }];
}];

signalBlock是一个返回signal的动作,这个动作在按钮被点击的时候执行,返回的signal连接一个RACReplaySubject,然后再executionSignal上订阅。

另外,在sendCompleted之前,按钮处于禁用状态。

每次执行动作都会发送一个新的signal,在executionSignals的next中注册这个signal的完成,而不是注册executionSignals的完成,事实上,它不会完成,因为它是代表了这个按钮可能被点击的序列。

相关文章

网友评论

  • 超_iOS:RACSignal 该用什么修饰符修饰啊

本文标题:RAC(Part2:SideEffect)

本文链接:https://www.haomeiwen.com/subject/xelgtttx.html