RAC 搜索示例: 部分代码: /* 第一步:[self.signInService checkSateSignal] : 是否允许搜索等判断 第二步:then直到 判断完成 ,返回self.username.rac_textSignal(输入信号) 第三步:(条件过滤1)filter:校验输入信号是否合法 第四步:(条件过滤2)throttle:连续输入信息时不请求,输入至少停顿(例如500毫 秒)才执行搜索,防止过于频繁 第五步:flattenMap:上面合法时,调用搜索请求接口,并将返回结果信号中的值 (Map直接返回信号本身) 第六步:回到主线程[RACScheduler mainThreadScheduler]:请求完成后回到主 线程,再进行下一步返回 第七步:subscribeNext:error: 请求数据对象(显示列表界面)或请求失败 */ - (void)demo1 { @weakify(self) [[[[[[[self.signInService checkSateSignal] then:^RACSignal *{ //条件判断 (checkSateSignal 成功后执行then) @strongify(self) return self.username.rac_textSignal; //输入信号 }] filter:^BOOL(NSString *value) { @strongify(self) return [self isValidSearchText:value]; //过滤输入信息是否符合 }] throttle:0.5] //节流: 当搜索内容改变时不是立马搜索,而是至少有一个时间 间隔 flattenMap:^RACStream *(NSString *value) {//flattenMap 将信号进行合 并处理后返回一个新的信号 return [self.signInService signInSignalWithTxt:value]; //发送请求 }] deliverOn:[RACScheduler mainThreadScheduler]] //回到主线程 subscribeNext:^(id data) { //返回结果 @strongify(self) NSDictionary *dataDic = data; NSLog(@"请求数据:%@",dataDic); //显示列表界面 self.desc.hidden = YES; self.tableView.hidden = NO; self.dataSource = [dataDic[@"list"] mutableCopy]; [self.tableView reloadData]; } error:^(NSError *error) { //失败 NSLog(@"请求失败:%@",error.domain); }]; } //输入是否合法 - (BOOL)isValidSearchText:(NSString *)text { if (text.length > 3) { return YES; } return NO; } 简单登录 /** :点击按钮时: 点击事件->创建信号 doNext:是直接跟在按钮点击事件的后面。而且doNext: block并没有返回值。 因为它是附加操作,并不改变事件本身。 doNext不会影响数据本身,也没有返回值,是一个附件功能:告诉你点击了 flattenMap: 信号中的信号 当管道信号量是[信号中的信号]时,解决的方法很简单,只需要把map操作改成 flattenMap就可以了: */ - (void)demo7 { //按钮否可以操作 [self demo6]; //可操作:将按钮点击事件创建一个信号,返回值默认是按钮本身UIButton RACSignal *clickSignal = [self.login rac_signalForControlEvents:UIControlEventTouchUpInside]; [[[clickSignal doNext:^(id x) { //doNext不会影响数据本身,也没有返回值,是一个附件功 能:告诉你点击了 self.login.enabled = NO;//点击按钮后不可使用 [self.login setTitle:@"登录中..." forState:0]; [SVProgressHUD showWithStatus:@"登录中..."]; }] flattenMap:^id(id x) {//信号中的信号 return [self signInSignal];//点击按钮发送异步请求 }] subscribeNext:^(NSNumber *signedIn) { self.login.enabled = YES;//执行完成后可点击 [self.login setTitle:@"登录" forState:0]; BOOL success = [signedIn boolValue]; [SVProgressHUD showSuccessWithStatus:@"登录成功"]; NSLog(success ? @"登录成功" : @"登录失败"); } error:^(NSError *error) { NSLog(@"失败"); [SVProgressHUD showSuccessWithStatus:@"登录失败"]; } completed:^{ //结束 }]; } 简单使用步骤: RACSiganl 信号类 1、创建信号 2、订阅信号 3、发送信号 4、取消订阅(图中未标明) 类比 整个过程就像是: 1.创建信号 : 创建一个对象,包含多个block方法 , 可以用来保存一个block (复制多分) 2.发送信号 : 请求成功后,使用对象的block来返回数据 3.订阅信号 : 调用对象block方法, block实现,获取返回的数据,并进行下一步处理 4.取消订阅 : 对象内部一些处理,内存释放, 好比 delegate = nil 简单理解就是将block比作信号 , 对其进行一系列封装 1.创建信号 创建信号本质就是创建了一个 RACDynamicSignal 类型的信号,并将传入的代 码块保存起来,留待以后调用 2.订阅信号 就是调用信号对象 Next 、Error 、 completed 等等方法 这些方法的内部都使用了一个类RACSubscriber(我们称为订阅者, RACPassthroughSubscriber 类型) ,来处理相应返回(保存block,等待请求成功 后返回数据时进行处理) 3.发送信号 发送信号就是: 请求数据后执行相应block来传递数据 (或者状态数据等) 但是不同调用函数逻辑又有区别 : sendNext : 一直传递数据 (类似Prograss 进度条) sendCompleted : 先取消该订阅, 再传递一次数据(后面不能再使用, 已结束) sendError : 同sendCompleted, 只是在错误状态下传递error数据 4.取消订阅信号 这个很好理解,就是不在接收数据了, 取消监听 取消订阅就是把订阅信号获得的disposable 进行dispose即可在调度器调度该部 分代码之前禁止调用 总结1 : 上面4个步骤简单介绍了信号RACSignal的使用 我们可以把RACSignal 类比 代理delegate功能 来理解 其他类大都是在RACSignal基础上进行封装,完成更多的功能 RACSubject 类的使用 : 1. RACSubject是在RACSignal基础上增加了功能, 用数组存放多个信号, 这样就 能接收多次订阅等等 2. RACSubject同样也是完成创建,订阅,发送,取消 信号的功能 3. RACSubject接收一次订阅,就好比是代理delegate功能 一对一 4. RACSubject接收多次订阅,好比是iOS的通知Notice 功能 一对多 RACCommand 类的使用 : RACCommand 封装一些功能,使得数据可以双向流通 ,不仅传递信号,还能接收对 象的状态信号,并进行一些额外的处理 RACCommand 可以发送信号时,传递一些参数, 同样可以完成操作前的条件判断 应用: 1. UIButton的enable属性,command开始执行是NO, 执行完了变成YES , 这样避 免重复点击 button.rac_command 2. 登录按钮, 登录判断 3. 网络请求 登录 @weakify(self) RAC(self.viewModel, userName) = self.userNameTF.rac_textSignal; RAC(self.viewModel, password) = self.passwordTF.rac_textSignal; self.loginBtn.rac_command = self.viewModel.loginCommand; [[self.viewModel.loginCommand executionSignals] subscribeNext:^(RACSignal *x) { @strongify(self) self.juhuaView.hidden = NO; [x subscribeNext:^(NSString *x) { self.juhuaView.hidden = YES; NSLog(@"%@",x); }]; }]; 总结2 : RACSubject 和 RACCommand 都是功能封装, 包括其他类 bind : 核心方法 1. bind操作方法实质上就是生成新的绑定信号,利用returnSignal作为中间信号 来改变源数据生成新的数据并执行新绑定信号的nextBlock代码块 2. 其他操作方法大都是都是以bind方法为核心的进行封装,来完成不同的需求,比 如map, flattenMap 等等 map 映射方法 本质是bind返回的信号: 在 map 方法中,返回的是 flattenMap 方法生成的信号,而 flattenMap 内部返 回的是 bind 方法返回的信号 map 映射方法的实质就是 bind 方法和 signal 深度封装,实际的信号流的流程 还是以 bind为核心 其他类似 总结3 : 很多方法的实质就是 bind 方法和 signal 深度封装 其他方法: skip -(void)skip{ //skip传入n跳过前面n个值 RACSubject * subject = [RACSubject subject]; [[subject skip:2] subscribeNext:^(id x) { NSLog(@"%@",x); }]; [subject sendNext:@1]; //跳过 [subject sendNext:@2];//跳过 [subject sendNext:@2];//执行 } take RACSubject * subject = [RACSubject subject]; //限制取值次数 - 取2次结束 [[subject take:2]subscribeNext:^(id x) { NSLog(@"%@",x); }]; [subject sendNext:@1];//执行 [subject sendNext:@2];//执行 [subject sendNext:@3];//跳过 takeLast RACSubject * subject = [RACSubject subject]; //取的是最后几个值 [[subject takeLast:2] subscribeNext:^(id x) { NSLog(@"%@",x); }]; [subject sendNext:@1];//无效 [subject sendNext:@2];//有效 [subject sendNext:@3]; //有效 [subject sendCompleted];//不写会取不到最后要的值 takeUntil RACSubject * subject = [RACSubject subject]; RACSubject * subject2 = [RACSubject subject]; [[subject takeUntil:subject2] subscribeNext:^(id x) { NSLog(@"%@",x); }]; [subject sendNext:@1]; [subject sendNext:@2]; [subject2 sendNext:@3];//调用后不会收到下边发送的内容 [subject2 sendNext:@4]; } ignore // ignore 忽略一些值 // ignoreValue 忽略所有的值 RACSubject * subject = [RACSubject subject]; RACSignal * ignoreSignal = [subject ignore:@2]; //忽略2 [ignoreSignal subscribeNext:^(id x) { NSLog(@"%@",x); }]; [subject sendNext:@2]; //被忽略 then 等待A信号结束再执行B,但是A的信号被忽略 //创建信号A, 必须执行sendCompleted,不然信号B没法执行 RACSignal * signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSLog(@“- 发送上部分请求- afn"); [subscriber sendNext:@"上部分数据”];//该信号被忽略 [subscriber sendCompleted]; return nil; }]; //创建信号B, 必须执行sendCompleted RACSignal * signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSLog(@"- 发送下部分请求- afn"); [subscriber sendNext:@"下部分数据"]; [subscriber sendCompleted]; return nil; }]; // 创建组合信号:signalA执行结束后执行B,但是A的信号值不返回 // then;忽略掉第一个信号的所有值 RACSignal * thenSignal = [signalA then:^RACSignal *{ return signalB; // 只会返回signalB的信号 }]; [thenSignal subscribeNext:^(id x) { NSLog(@"%@",x); }]; 打印结果 : - 发送上部分请求- afn - 发送下部分请求- afn 下部分数据 concat 等待A信号结束再执行B,A的信号不会被忽略,先返回A,再返回B 替换上面then代码为concat //**-注意-**:concat,第一个信号必须要调用sendCompleted RACSignal * concatSignal = [signalA concat:signalB]; [concatSignal subscribeNext:^(id x) { NSLog(@"%@",x); }]; 打印结果 : - 发送上部分请求- afn 上部分数据 - 发送下部分请求- afn 下部分数据 distinctUntilChanged RACSubject * subject = [RACSubject subject]; //处理解决相同值没必要多次被订阅 [[subject distinctUntilChanged] subscribeNext:^(id x) { NSLog(@"%@",x); }]; [subject sendNext:@1]; [subject sendNext:@2]; [subject sendNext:@2];//不会被订阅 doNext flattenMap zipWith //zipWith:把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时, 并且把两个信号的内容合并成一个元祖,才会触发压缩流的next事件。 //使用场:当一个界面多个请求的时候,要等所有请求完成才更新UI RACSubject * signalA = [RACSubject subject]; RACSubject * signalB = [RACSubject subject]; RACSignal * zipSignal = [signalA zipWith:signalB]; //跟压缩的顺序有关 先A 后B //等所有信号都发送内容的时候才会调用 [zipSignal subscribeNext:^(id x) { NSLog(@"%@",x); }]; //signalB 结束后调用 [signalA sendNext:@1]; [signalB sendNext:@2]; merge RACSubject * signalA = [RACSubject subject]; RACSubject * signalB = [RACSubject subject]; //组合信号:多个信号合并成一个信号,任何一个信号有新值就会调用 ,任何一 个信号请求完成都会被订阅到 RACSignal * mergeSignal = [signalA merge:signalB]; [mergeSignal subscribeNext:^(id x) { NSLog(@"%@",x); }]; // 发送信号 - 交换位置则数据结果顺序也会交换 [signalB sendNext:@2]; [signalA sendNext:@1]; map 返回合成的信号 combineLatest: reduce: reduce 返回多信号元组(A,B) https://www.jianshu.com/p/c2fba4f1e32e filter [[textField.rac_textSignal filter:^BOOL(id value) { return [value length] > 5;//返回值就是过滤条件 满足才能获取到 }] subscribeNext:^(id x) { NSLog(@"%@",x); }];