理解RAC
Framework Overview
Stream
RACStream是一个抽象类,stream里的流可以被立刻获取也可以在未来的某个时刻获取(同步或者异步),但是必须是有序的,不能在没有evaluating or waiting 第一个数据的时候就去获取第二个数据。
Stream是monad的概念。Stream只是抽象类,subclass后的signal或者sequence更强大。
Signal
RACSignal是一个push-driven stream 也就是在每次信号中的出现新的数据时,所有的订阅者都会自动接受到最新的值;
信号承载的是未来将会发送过来的数据,在某个时刻,数据会发送给signal,signal会把数据转发给subscriber。
对于subscriber来说,signal推送过来的事件有三种类型
next:下一个值,也就是data or value
error:错误,意思是可能会包含一个NSError,但是error必须被单独处理,不被包含在value里
completed:意思是这signal寿终正寝,后面不会有任何事件下发了
RAC对大部分的UIKit控件进行了扩展,支持RACSignal。主要包括UITextView,UITextField,UISlider,UIStepper,UIControl,UISwitch,UIActionSheet,UIAlertView,UIDatePicker。
Subscription
订阅者是一个正在等待事件或者有能力等待事件的东西,可以是任何东西,遵循RACSubscriber这个协议。一个subscription在调用-subscribeNext:error:completed:的时候创建,大部分的RACStream的operator操作也会创建subscription。一个subscription会持有那个signal,并且当下发error或者complete的时候被自动丢弃。当然也可以手动丢弃
Subjects
RACSubject,是一个被手动控制的signal。RACSubject可以被看作一个signal的mutable版本,可以用与桥接非RAC的code到RAC的环境下。
subject有replay的额外功能。
Commands
一个RACCommand,创建并订阅一个RACSignal,用来perform一些side effect的动作。
提供了disabled的便捷方法,对应一些UI元素的状态。
常见的有UIButton,UIBarButtonItem,UIRefreshControl
Connections
RACMulticastConnection,是一个subscription,在一个signal的所有的subscriber中共享。RACSignal默认是冷信号,意味着每一个订阅者订阅(一个新的subscription添加)的时候都会触发做一些事情。这是符合预期的,但是有一些情况下我们希望避免这种行为,比如signal做的事情消耗太多或者有side effect。
通过RACSignal的-publish or -multicast:来创建一个connection,并且保证在connection和signal之间只创建了一个隐式的subscription,不论connection本身被订阅了多少次。
Sequences
sequence是一个*pull-driven stream。*在signal的value改变时并不会通知使用当前序列的对象,只有使用者再次从这个 RACSequence 对象中获取数据才能更新,它的更新是需要使用者自己拉取的
存在eagerSequence和lazySequence两种,一般用eager即可。
RACSequence和RACSignal可以相互转换
RACSequence *sequence = @[@1, @2, @3].rac_sequence;
RACSignal *signal = sequence.signal;[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);}];
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
[subscriber sendNext:@3];
[subscriber sendCompleted];
return nil;}];NSLog(@"%@", signal.toArray.rac_sequence);
Disposables
用来cancellation and resource cleanup。
最常见的是取消对一个signal的订阅。
Schedulers
类似于GCD的queue,但是支持取消【GCD也支持,相对繁琐一些】
不提供sync方法,防止思索。
鼓励使用signal operator而不是用block完成。
Value types
RACTuple is a small, constant-sized collection that can contain nil (represented by RACTupleNil). It is generally used to represent the combined values of multiple streams.
RACUnit is a singleton “empty” value. It is used as a value in a stream for those times when more meaningful data doesn’t exist.
RACEvent represents any signal event as a single value. It is primarily used by the -materialize method of RACSignal.
Basic operations
Performing side effect with signal
大部分的signal都是cold的,在没有subscription之前不会做任何事情。依赖Subscription,signal或者subscriber可以做一些side effect的事情,比如打印log,网络请求,更新UI等。
Subscription
- subscribe操作让你可以接入这个signal当前和未来的值。
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;
// Outputs: A B C D E F G H I
[letters subscribeNext:^(NSString *x) {
NSLog(@"%@", x);
}];
Perform side effect like this:
__block unsigned subscriptions = 0;
RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
subscriptions++;
[subscriber sendCompleted];
return nil;
}];
// Outputs:// subscription 1
[loggingSignal subscribeCompleted:^{
NSLog(@"subscription %u", subscriptions);
}];
// Outputs:// subscription 2
[loggingSignal subscribeCompleted:^{
NSLog(@"subscription %u", subscriptions);
}];
Side effect就是一个没创建一次loggingSignal就会增加一次subscriptions这个变量的值,这个值会被subscribe的时候使用,所以就是side effect。要特别谨慎side effect的使用。
除了上面这种直接在signal创建的时候使用的side effect的方法,我们还可以【注入】side effect。
使用-do方法,可以让一个sinal在每次被订阅的时候都执行一次。
__block unsigned subscriptions = 0;
RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
subscriptions++;
[subscriber sendCompleted];
return nil;
}];
// Does not output anything yet
//在这里注入了
loggingSignal = [loggingSignal doCompleted:^{
NSLog(@"about to complete subscription %u", subscriptions);
}];
// Outputs:
// about to complete subscription 1
// subscription 1
[loggingSignal subscribeCompleted:^{
NSLog(@"subscription %u", subscriptions);
}];
Transforming streams
进行单一到单一的变换
Mapping
Filtering
Combining streams
组合多个流为一个流
Concat
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *concatenated = [letters concat:numbers];
Flatten
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *sequenceOfSequences = @[ letters, numbers ].rac_sequence;
// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *flattened = [sequenceOfSequences flatten];
Mapping and flattening
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
// Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
RACSequence *extended = [numbers flattenMap:^(NSString *num) {
return @[ num, num ].rac_sequence;
}];
// Contains: 1_ 3_ 5_ 7_ 9_
RACSequence *edited = [numbers flattenMap:^(NSString *num) {
if (num.intValue % 2 == 0) {
return [RACSequence empty];
} else {
NSString *newNum = [num stringByAppendingString:@"_"];
return [RACSequence return:newNum];
}
}];
Combining signals
Sequencing
Merging
Combining latest values
Switching