RACSignal:
一:创建方法:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe;
+ (RACSignal *)error:(NSError *)error;
+ (RACSignal *)return:(ValueType)value;
+ (RACSignal *)empty;
二:操作相关:
1.timeOut:超时,可以让一个信号在一定的时间后,自动报错。
[[[RACSignal createSignal:^RACDisposable *(id subscriber) {
[[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
[subscriber sendNext:@"rac"];
[subscriber sendCompleted];
}];
return nil;
}] timeout:2 onScheduler:[RACScheduler mainThreadScheduler]]
subscribeNext:^(id x) {
//TODO
} error:^(NSError *error) {
//TODO
} completed:^{
//TODO
}];
2.then:有两部分数据:想让上部分先进行网络请求但是过滤掉数据,然后进行下部分的,拿到下部分数据
// 创建信号A
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
NSLog(@"----发送上部分请求---");
[subscriber sendNext:@"上部分数据"];
[subscriber sendCompleted]; // 必须调用sendCompleted方法!
return nil;
}];
// 创建信号B
RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
// 发送请求
NSLog(@"--发送下部分请求--");
[subscriber sendNext:@"下部分数据"];
[subscriber sendCompleted];
return nil;
}];
// 创建组合信号
RACSignal *thenSignal = [signalA then:^RACSignal *{
return signalB;
}];
// 订阅信号
[thenSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
3.zipWith:把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时,并且把两个信号的内容合并成一个元组,才会触发压缩流的next事件。
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@1];
return nil;
}];
RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@2];
return nil;
}];
// 压缩信号A,信号B
RACSignal *zipSignal = [signalA zipWith:signalB];
[zipSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
元组内元素的顺序不会变,跟发送的顺序无关,而是跟压缩的顺序有关[signalA zipWith:signalB]---先是A后是B
底层实现:
1.定义压缩信号,内部就会自动订阅signalA,signalB
2.每当signalA或者signalB发出信号,就会判断signalA,signalB有没有发出个信号,有就会把最近发出的信号都包装成元组发出。
等同于下面的写法
[[RACSignal combineLatestWith:@[signalA, signalB] subscribeNext:^(id x) {
//TODO
}];
4.retry重试只要失败,就会重新执行创建信号中的block,直到成功.
__block int i = 0;
[[[RACSignal createSignal:^RACDisposable *(id subscriber) {
if (i == 10) {
[subscriber sendNext:@1];
}else{
NSLog(@"接收到错误");
[subscriber sendError:nil];
}
i++;
return nil;
}] retry] subscribeNext:^(id x) {
NSLog(@"%@",x);
} error:^(NSError *error) {
}];
5.throttle节流:在一定时间(1秒)内,不接收任何信号内容,过了这个时间(1秒)获取最后发送的信号内容发出
RACSubject *signal = [RACSubject subject];
[[signal throttle:1] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[signal sendNext:@"test"];
6.interval定时:每隔一段时间发出信号(基本上算是rac定时器了)
[[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
7.delay:延迟发送
①RACSignal *signal = [[[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@1];
return nil;
}] delay:2] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
②[[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{
//TODO
}];
8.take:从开始一共取N次的信号。takeLast:取最后N次的信号,前提条件,订阅者必须调用完成,因为只有完成,就知道总共有多少信号.takeUntil:获取信号直到某个信号执行完成
RACSubject *signal = [RACSubject subject];
[[signal take:1] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[signal sendNext:@1];
[signal sendNext:@2];
9.concat:有两部分数据:想让上部分先执行,完了之后再让下部分执行(都可获取值), 如果A发送失败,B也不会执行。A和B是依赖关系
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"上部分数据"];
[subscriber sendCompleted]; // 必须要调用sendCompleted方法!
return nil;
}];
RACSignal *signalsB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"下部分数据"];
return nil;
}];
// concat:按顺序去链接
RACSignal *concatSignal = [signalA concat:signalsB];
// 订阅组合信号
[concatSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
10.RAC线程切换
RACScheduler *backgroundScheduler = [RACScheduler scheduler];
RACSignal *testSignal = [[RACSignal
createSignal:^RACDisposable *(id subscriber) {
// 这段代码会运行在子线程
[subscriber sendNext:@1];
[subscriber sendCompleted];
return nil;
}]
subscribeOn:backgroundScheduler];
[[testSignal deliverOn: [RACScheduler mainThreadScheduler]] subscribeNext:^(id _Nullable x) {
NSLog(@"在主线程执行");
}];
takeLast表示倒数的前两次
接下来是几个block回调方法
takeWhileBlock BOOL值,意思是当返回YES的时候,订阅者才能收到信号
skipWhileBlock BOOL值,意思是当返回YES的时候,订阅者就会跳过信号,NO的时候才接受
skipUntilBlock BOOL值,意思是 返回NO的时候,不会收到消息, 直到返回YES的时候才开始收消息。
distinctUntilChanged 表示两个消息相同的时候,只会发送一个请求
11.merge: 把多个信号合并为一个信号,任何一个信号有新值的时候就会调用
RACSubject *signalA = [RACSubject subject];
RACSubject *signalB = [RACSubject subject];
RACSignal *signals = [signalA merge:signalB];
[signals subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[signalA sendNext:@1];
[signalB sendNext:@2];
[signalB sendNext:@3];
12.combineLatest: reduce:把validUsernameSignal和validPasswordSignal产生的最新的值聚合在一起,并生成一个新的信号。每次这两个源信号的任何一个产生新值时,reduce block都会执行,block的返回值会发给下一个信号
RACSignal *signUpActiveSignal =
[RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid) {
return @([usernameValid boolValue] && [passwordValid boolValue]);
}];
combineLatest:将多个信号合并起来,并且拿到各个信号的最新的值,必须每个合并的signal至少都有过一次sendNext,才会触发合并的信号。
reduce聚合:用于信号发出的内容是元组,把信号发出元组的值聚合成一个值。reduceblcok中的参数,有多少信号组合,reduceblcok就有多少参数,每个参数就是之前信号发出的内容
reduceblcok的返回值:聚合信号之后的内容。
rac_liftSelector:withSignals 也是类似,当signalA和signalB都至少sendNext过一次,接下来只要其中任意一个signal有了新的内容,相应方法就会自动被调用。
13.skip:表示输入第几次,不会被监听到,跳过第几次发出的信号
[[_textField.rac_textSignal skip:1] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
14.distinctUntilChanged:过滤,当上一次和当前的值不一样,就会发出内容
[[_textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
15.filter:过滤信号,使用它可以获取满足条件的信号
[_textField.rac_textSignal filter:^BOOL(NSString *value) {
return value.length > 3;
}];
16.ignore:忽略某些值的信号
[[_textField.rac_textSignal ignore:@"1"] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
17.startWith:startWith:@"123"等同于[subscriber sendNext:@"123"] 也就是第一个发送
RACSignal * signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
//[subscriber sendNext:@"123"];//startWith:@"123"等同于这句话
[subscriber sendNext:@"rac"];
[subscriber sendCompleted];
return nil;
}] startWith:@"123"];
[signal subscribeNext:^(id x) {
}];
18.switchToLatest:只能用于signalOfSignals(信号的信号)
RACSubject *signalOfSignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];
[signalOfSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[signalOfSignals sendNext:signal];
[signal sendNext:@1];
19.
doNext: 执行Next之前,会先执行这个Block
doCompleted: 执行sendCompleted之前,会先执行这个Block
[[[[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@1];
[subscriber sendCompleted];
return nil;
}] doNext:^(id x) {
// 执行[subscriber sendNext:@1];之前会调用这个Block
}] doCompleted:^{
// 执行[subscriber sendCompleted];之前会调用这个Block
}]];
一个页面多个网络请求:
①rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组),每一个signal都至少sendNext过一次,就会去触发第一个selector参数的方法
RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"发送请求1"];
return nil;
}];
RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"发送请求2"];
return nil;
}];
// 使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。
[self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];
②接口串联
@weakify(self)
RACSignal *finalSignal = [[self fetchData]
flattenMap:^RACSignal *(NSString *dataResult) {
@strongify(self)
return [self fetchData2:dataResult];
}];
RACCommand篇:
一般用于:1:button点击 2:包装网络请求接口
一: RACCommand的创建有两种形式:
- (id)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock;
- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock;
注意:
1.伴随着command一起构建的signal,记得要在操作完成后发送完成消息以表示其执行完了,[subscriber sendCompleted];否则不能再执行此command。
2.UIButton中有属性rac_command用于绑定一个已经创建好的command,当使用第二种方式创建command时,button的enable属性会随command的可执行性而改变,意思是当传递布尔事件的信号传递了真值事件,按钮才可使用。另外,当你按下按钮,command开始执行时,按钮的enable被自动设置成了NO,除非command执行完了。
3.当button的rac_command已经绑定了某个command,而这个command又是以第二种方式初始化,那么你就不能动态改变button的enable,如:RAC(self.button, enable) = someSignal;这样子运行起来会报错。
二:执行RACCommand:
- (RACSignal *)execute:(id)input;
三:订阅RACCommand:
[[[command executionSignals] switchToLatest]
subscribeNext:^(id x) {
// TODO
}];
四:在对command进行错误处理的时候,不使用subscribeError:对command的executionSignals进行错误的订阅,executionSignals这个信号是不会发送error事件的,当command包裹的信号发送error事件时,用到command的一个属性:errors,可以对错误进行订阅:
[command.errors
subscribeNext:^(NSError *x) {
//TODO
}];
也可以通过executing属性判断是否正在执行
注意事项篇:
1.RACSignal *signalReplay = signal.replay;
实现多个订阅者没有副作用的效果
2.RACSubject和RACReplaySubject的区别
RACSubject必须要先订阅信号之后才能发送信号,而RACReplaySubject可以先发送信号后订阅
3.避免副作用效果处理(RACMulticastConnection)
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
NSLog(@"发送请求");
[subscriber sendNext:@"ws"];
return nil;
}];
RACMulticastConnection *connection = [signal publish];
[connection.signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
[connection connect];
4.RACObserve默认会含有self,所以在RAC block内使用RACObserve时一定要用@weakify(self);@strongify(self);处理,否则会导致内存泄漏
5.RACSubject被map之后 在发送next信号时一定要发送complete方法,否则会导致内存泄漏。所以不管任何场景下都要sendComplete/sendError
6.ViewModel里用来保存数据的数组,不能使用NSMutableArray。原因是RAC是基于KVO的,而NSMutableArray的Add和Remove方法并不会给KVO发送通知,因此对NSMutableArray进行RACObserve时,并不会达到我们想要的结果。(同理其他Mutable的也都不能用)
7.flattenmap与map的区别是: block中返回的东西不同,flattenmap返回的是signal信号。flattenMap方法,实际上是根据前一个信号传递进来的参数重新建立了一个信号,这个参数,可能会在创建信号的时候用到,也有可能根本用不到。
8.RACScheduler RAC中的队列,用GCD封装的
9.RACBehaviorSubject最重要的特性就是在订阅时,向最新的订阅者发送之前的消息,RACReplaySubject 相当于一个自带 buffer 的 RACBehaviorSubject,它可以在每次有新的订阅者订阅之后发送之前的全部消息。
10.deliverOn: 内容传递切换到制定线程中,副作用在原来线程中。
subscribeOn: 内容传递和副作用都会切换到制定线程中。
11.热信号是主动的,即使你没有订阅事件,它仍然会时刻推送。而冷信号是被动的,只有当你订阅的时候,它才会发送消息。热信号可以有多个订阅者,是一对多,而冷信号只能一对一,当有不同的订阅者,消息会从新完整发送。
替换原生方法:
1.NSData
rac_readContentsOfURL: options: scheduler: 比oc多出线程设置
2.NSDictionary
rac_keySequence key 集合
rac_valueSequence value 集合
3.RACTuple元组
RACTuple *tuple = RACTuplePack(@1,@2,@4);
// 宏的参数类型要和元组中元素类型一致, 右边为要解析的元组。
RACTupleUnpack_(NSNumber *num1, NSNumber *num2, NSNumber * num3) = tuple;
NSLog(@"%@ %@ %@", num1, num2, num3);
4.代理
①之前都是需要通过代理监听,给红色View添加一个代理属性,点击按钮的时候,通知代理做事情
rac_signalForSelector:把调用某个对象的方法的信息转换成信号,就要调用这个方法,就会发送信号。
这里表示只要redV调用btnClick:就会发出信号,订阅就好了。
[[redV rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
NSLog(@"点击红色按钮");
}];
②
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * tuple) {
//TODO
}];
eg:
@weakify(self)
self.proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextFieldDelegate)];
[[self.proxy rac_signalForSelector:@selector(textFieldShouldReturn:)]
subscribeNext:^(id x) {
@strongify(self)
}];
self.nameText.delegate = (id)self.proxy;
5.KVO
①
[[redV rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
②RACObserve(<#TARGET#>, <#KEYPATH#>)
6.监听事件
[[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"按钮被点击了");
}];
7.通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"键盘弹出");
}];
8.手势
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] init];
[[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
//TODO
}];
[self.view addGestureRecognizer:tap];
9.遍历数组
NSArray *numbers = @[@1,@2,@3,@4];
[numbers.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
10.遍历字典,遍历出来的键值对会包装成RACTuple
NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
// 解包元组,会把元组的值,按顺序给参数里面的变量赋值
RACTupleUnpack(NSString *key,NSString *value) = x;
// 相当于以下写法
// NSString *key = x[0];
//NSString *value = x[1];
NSLog(@"%@ %@",key,value);
}];
双向绑定:
RAC(<#TARGET, ...#>)//单向绑定
RACChannelTo(<#TARGET, ...#>)//双向绑定
RACChannelTo(self, filmType, @(ASHRecipeFilmTypeColourNegative)) 第三个参数是指,如果值的变化中出现 nil,那么就会使用这个值来代替,相当于一个默认值。
RACChannelTo用的是RACKVOChannel实现的
eg:
RACChannelTo(self.someLabel, text) = RACChannelTo(self.viewModel, someProperty);
[self.textField.rac_newTextChannel subscribe:self.viewModel.someChannel];
[self.viewModel.someChannel subscribe:self.textField.rac_newTextChannel];
RACChannelTo(self, reviewID) = self.viewModel.someChannel;
[self.textField.rac_newTextChannel subscribe:self.anotherTextField.rac_newTextChannel];
[self.anotherTextField.rac_newTextChannel subscribe:self.textField.rac_newTextChannel];
[[RACKVOChannel alloc] initWithTarget:view keyPath:@"property" nilValue:nil][@"followingTerminal"]
= [[RACKVOChannel alloc] initWithTarget:model keyPath:@"property" nilValue:nil][@"followingTerminal"];
与RACChannelTo(view, property) = RACChannelTo(model, property);等价