信号(signal)— RACSignal类
1. 一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。
2. 事件类型:
next:发送数据到下一个管道
error:发送数据失败
completed:发送数据完成
注意:
信号类(RACSiganl),只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。
默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。
如何订阅信号:调用信号RACSignal的subscribeNext就能订阅。
``` // 创建信号 RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { // 发送信号 [subscriber sendNext:@"发送信号"]; [subscriber sendCompleted]; return [RACDisposable disposableWithBlock:^{ // 信号什么时候被取消:1.自动取消,当一个信号的订阅者被销毁的时候,就会自动取消订阅 2.主动取消 // block调用时刻:一旦一个信号,被取消订阅的时候就会调用 // block作用:当信号取消订阅,用于清空一些资源 NSLog(@"信号被取消订阅"); }]; }]; // 订阅信号 RACDisposable *disposable = [signal1 subscribeNext:^(id x) { NSString *text = x; NSLog(@"得到订阅值:%@",text); } completed:^{ NSLog(@"******完成******"); }]; // 取消订阅(主动取消) [disposable dispose]; ```
3. 过滤 — Filter
说明:过滤信号,使用它可以获取满足条件的信号,举个形象的比喻就是一张可以自由设置网口大小的渔网,根据自己需要,对网口进行设置就可以捕到特定规格的鱼。
用法:在用户登录时,我们需要关心用户名长度是否符合要求,比如要求字符长度超过10才可以往下面执行
``` [[self.textTF.rac_textSignal filter:^BOOL(NSString *value) { return value.length > 10; }] subscribeNext:^(id x) { NSLog(@"过滤后的值:%@",x); }]; ```
4. 映射 — Map :把源信号内容映射成新的内容,简单点说就是将数据改成自己想要的数据。
map操作通过block改变了事件的数据。map从上一个next事件接收数据,通过执行block把返回值传给下一个next事件。(map 可以返回任何oc对象)在上面的代码中,map以NSString为输入,取字符串的长度,返回一个NSNumber。
注: 能看到map操作之后的步骤收到的都是NSNumber实例。你可以使用map操作来把接收的数据转换成想要的类型,只要它是个对象。
RACSignal *usernameLengthSignal = [[self.usernameTextField.rac_textSignal map:^id(NSString *value) { return @(value.length); }];
5. 状态推导 — RAC()
说明:用于给某个对象的某个属性绑定。
用法:比如只要文本框文字改变,就会修改label的文字
RAC(self.labelView,text) = _textField.rac_textSignal;
6. 聚合信号
说明:聚合任意数量的信号,然后生成一个新的信号
用法:比如登录按钮只有当用户名和密码输入框的输入都有效时才能进行点击
7. map —> flattenMap : 用于把源信号内容映射成新的内容。
flattenMap:
// 监听文本框的内容改变,把结构重新映射成一个新值. // flattenMap作用:把源信号的内容映射成一个新的信号,信号可以是任意类型。 // flattenMap使用步骤: // 1.传入一个block,block类型是返回值RACStream,参数value // 2.参数value就是源信号的内容,拿到源信号的内容做处理 // 3.包装成RACReturnSignal信号,返回出去。 // flattenMap底层实现: // 0.flattenMap内部调用bind方法实现的,flattenMap中block的返回值,会作为bind中bindBlock的返回值。 // 1.当订阅绑定信号,就会生成bindBlock。 // 2.当源信号发送内容,就会调用bindBlock(value, *stop) // 3.调用bindBlock,内部就会调用flattenMap的block,flattenMap的block作用:就是把处理好的数据包装成信号。 // 4.返回的信号最终会作为bindBlock中的返回信号,当做bindBlock的返回信号。 // 5.订阅bindBlock的返回信号,就会拿到绑定信号的订阅者,把处理完成的信号内容发送出来。 [[_textField.rac_textSignal flattenMap:^RACStream *(id value) { // block什么时候 : 源信号发出的时候,就会调用这个block。 // block作用 : 改变源信号的内容。 // 返回值:绑定信号的内容. return [RACReturnSignal return:[NSString stringWithFormat:@"输出:%@",value]]; }] subscribeNext:^(id x) { // 订阅绑定信号,每当源信号发送内容,做完处理,就会调用这个block。 NSLog(@"%@",x); }];
map:
// 监听文本框的内容改变,把结构重新映射成一个新值. // Map作用:把源信号的值映射成一个新的值 // Map使用步骤: // 1.传入一个block,类型是返回对象,参数是value // 2.value就是源信号的内容,直接拿到源信号的内容做处理 // 3.把处理好的内容,直接返回就好了,不用包装成信号,返回的值,就是映射的值。 // Map底层实现: // 0.Map底层其实是调用flatternMap,Map中block中的返回的值会作为flatternMap中block中的值。 // 1.当订阅绑定信号,就会生成bindBlock。 // 3.当源信号发送内容,就会调用bindBlock(value, *stop) // 4.调用bindBlock,内部就会调用flattenMap的block // 5.flattenMap的block内部会调用Map中的block,把Map中的block返回的内容包装成返回的信号。 // 5.返回的信号最终会作为bindBlock中的返回信号,当做bindBlock的返回信号。 // 6.订阅bindBlock的返回信号,就会拿到绑定信号的订阅者,把处理完成的信号内容发送出来。 [[_textField.rac_textSignal map:^id(id value) { // 当源信号发出,就会调用这个block,修改源信号的内容 // 返回值:就是处理完源信号的内容。 return [NSString stringWithFormat:@"输出:%@",value]; }] subscribeNext:^(id x) { NSLog(@"%@",x); }];
FlatternMap和Map的区别:
FlatternMap中的Block返回信号。
Map中的Block返回对象。
开发中,如果信号发出的值不是信号,映射一般使用Map
开发中,如果信号发出的值是信号,映射一般使用FlatternMap
// 创建信号中的信号 RACSubject *signalOfsignals = [RACSubject subject]; RACSubject *signal = [RACSubject subject]; [[signalOfsignals flattenMap:^RACStream *(id value) { // 当signalOfsignals的signals发出信号才会调用 NSLog(@"3. ***"); return value; }] subscribeNext:^(id x) { // 只有signalOfsignals的signal发出信号才会调用,因为内部订阅了bindBlock中返回的信号,也就是flattenMap返回的信号。 // 也就是flattenMap返回的信号发出内容,才会调用。 NSLog(@"%@4. 最后输出",x); }]; // 信号的信号发送信号 [signalOfsignals sendNext:signal]; // 信号发送内容 [signal sendNext:@1]; // signal 发送内容 才会激活这个signal signalOfsignals 发送信号才会激活这个信号 然后走到flattenMap这个里面 现在订阅这这信号但是不会触发 只有falttenMap 返回信号后才会触发这个信号 最后输出 最后 signal 发送信号 激活了 signal 由于 signalOfsignal 发送了signal 当sinal被激活后 signalOfsignal也激活了信号 然后往下面一步步执行...
8. 添加附加操作(doNext:)
说明:执行Next之前,会先执行这个Block,简单说就是在一段逻辑执行前进行拦截,然后先执行一段特别操作,再操作接下来的逻辑
- (RACSignal *)doNext:(void (^)(id x))block
用法:还是拿登录的场景来说,当登录service正在校验用户名和密码时,登录按钮应该是不可点击的。这会防止用户多次执行登录操作。还有,如果登录失败了,用户再次尝试登录时,应该隐藏错误信息。