函数编程里一切计算都是为了求值,没有副作用是一个显著地特征。从实用角度出发,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的完成,事实上,它不会完成,因为它是代表了这个按钮可能被点击的序列。
网友评论