读 顾语流年 RAC+MVVM的项目实例演练 项目有感
向作者说的再写项目之前先想好架构,将一些东西封装进基类是比较省事省力的方法
M很正常
V controller中的view做了解耦使用rac将逻辑放到了vc的ViewModel中,比如将tableView、collectionView绑定到VM,然后再VM中实现其代理方法 。再使用RACSubject做了一些方法的回调(页面跳转逻辑)到Controller中。 但是cell(tableViewCell、collectionViewCell)的逻辑没有做分离,还是存在于cell中,只是使用RAC做了赋值操作、回调操作(向ViewModel逆向传值 代理方法写在VM中)
C controller负责做view的加载、接口的调用时机、将view绑定到ViewModel中 还有具体的跳转代码写在C中
VM 在一个VC中可以根据不同的业务持有多个VM,不同的数据请求也可以在不同的VM中直接请求。 直接在VM中请求接口,同时做了模型转换 直接持有了M
RACCommand 负责数据请求
RACSubject 负责逻辑操作:赋值操作、页面跳转操作、逆向值传递、点击事件的操作
对已赋值操作可以使用宏进行信号绑定
RAC(self.label, text) =self.textField.rac_textSignal;
GOOG Code
//获取首页列表数据
- (void)requestHomeListInfo {
@weakify(self);
_homeListCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
@strongify(self);
if(!self.firstLoadData) {
return[RACSignalempty];
}
RACSignal*requestSignal = [RACSignalcreateSignal:^RACDisposable*(id subscriber) {
[[WLNetworkToolsharedInstance]loadHomeListData:[inputintValue]success:^(idresponse) {
self.firstLoadData=NO;
[subscribersendNext:response];
[subscribersendCompleted];
}failure:^(NSError*error) {
[subscribersendError:error];
[subscribersendCompleted];
}];
returnnil;
}];
return[requestSignalmap:^id(NSDictionary*value) {
NSArray*data = value[@"data"][@"items"];
NSArray*modelsArray = [[data.rac_sequencemap:^id(idvalue) {
return[WLHomeListModelmj_objectWithKeyValues:value];
}]array];
//这一步刷新列表可以放到这里,也可以放到控制器里面
NSLog(@"请求首页列表数据成功 %@",modelsArray);
if(modelsArray && modelsArray.count>0) {
[self.homeListArrayremoveAllObjects];
[self.homeListArrayaddObjectsFromArray:modelsArray];
[self.tableViewreloadData];
}
returnmodelsArray;
}];
}];
}
后记
// RACSignal使用步骤
/**
*1.创建信号
*2.订阅信号(订阅信号后,才会被激活)
*3.发送信号
/RACSignal底层实现
1、创建信号,首先把didSubscribe保存到信号中,还不会触发
2、当信号被订阅,也就是调用signal的subscribrNext:nextBlock, subscribeNext内部会创建订阅者subscriber,并把nextBlock保存到subscriber中,subscribNext内部会调用signal的didSubscribe
3、signal的didsubcribe中调用【subscriber sendNext:@1】,sendNext底层其实就是执行subscriber的nextBlock。
*/
// 1.创建信号
RACSignal*signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
// block调用时刻:每当有订阅者订阅信号,就会调用block
// 2 发送信号
[subscribersendNext:@1];
NSLog(@"111-------------");
// 如果不再发送数据,最好调用发送信号完成方法,内部会自动调用[RACDisposable disposable]
[subscribersendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"111信号被销毁");
}];
}];
// 3.订阅信号,才会激活信号
[signalsubscribeNext:^(idx) {
NSLog(@"111接受到数据:%@",x);
}];
[signalsubscribeNext:^(idx) {
NSLog(@"222jiehsoushuji---%@",x);
}];
// RACSubject使用步骤
/**
1、创建信号 【RACSubject subject】,跟RACSignal不一样,创建信号时没有block。
2、订阅信号 -(RACDisposable *)subscribeNext:(void(^)(id x)nextBlock)
3、发送信号 sengNext:(id Value)
// RACSubject : 底层实现跟RACSignal不一样
1、调用subscribeNext订阅信号,只是把订阅者保存起来,并且订阅者的nextBlock已经赋值了。
2、调用sendNext发送信号,遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock
*/
// 1、创建信号
RACSubject *subject = [RACSubject subject];
// 2、订阅信号
[subjectsubscribeNext:^(idx) {
// block调用时刻:当信号发出新值,就会调用
NSLog(@"第一个订阅者:%@",x);
}];
[subjectsubscribeNext:^(idx) {
NSLog(@"第二个订阅者:%@",x);
}];
// 3、发送信号
[subjectsendNext:@1];
[subjectsendNext:@2];
// RACRepalySubject使用步骤
/**
1、创建信号 [RACReplaySubject subject] ,跟RACSignal不一样,创建信号时没有block
2、可以先发送信号,再订阅信号,RACSubject不可以!!!
*订阅信号 -(RACDisposable)subscribeNext:(void(^)(id x))nextBlock
*发送信号 sendNext:(id)value
RACReplaySubject:底层实现和RACSubject不一样
1、调用sendNext发送信号,把值保存起来,然后遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock
2、调用subscribeNext订阅信号,遍历所有保存的值,一个一个调用订阅者的nextBlock
如果想当一个信号被订阅,就重复播放之前所有值,需要先发送信号,再订阅信号
也就是先保存值,再订阅值
*/
// 1、创建信号
RACReplaySubject *replaySubject = [RACReplaySubject replaySubjectWithCapacity:2];
// 2、发送信号
[replaySubjectsendNext:@1];
[replaySubjectsendNext:@2];
// 3、订阅信号
[replaySubjectsubscribeNext:^(idx) {
NSLog(@"第一个订阅者收到的数据%@",x);
}];
[replaySubjectsubscribeNext:^(idx) {
NSLog(@"第二个订阅者收到的数据%@",x);
}];
/**
// 先订阅,后发送信号
2016-08-29 16:18:23.154 RACDemo[5507:185680] 第一个订阅者收到的数据1
2016-08-29 16:18:23.154 RACDemo[5507:185680] 第二个订阅者收到的数据1
2016-08-29 16:18:23.154 RACDemo[5507:185680] 第一个订阅者收到的数据2
2016-08-29 16:18:23.155 RACDemo[5507:185680] 第二个订阅者收到的数据2
*/
/**
// 先发送信号,再订阅
2016-08-29 16:19:43.945 RACDemo[5641:186739] 第一个订阅者收到的数据1
2016-08-29 16:19:43.945 RACDemo[5641:186739] 第一个订阅者收到的数据2
2016-08-29 16:19:43.946 RACDemo[5641:186739] 第二个订阅者收到的数据1
2016-08-29 16:19:43.946 RACDemo[5641:186739] 第二个订阅者收到的数据2
*/
// 1、遍历数组
NSArray*array =@[@1,@2,@3];
// 1、把数组转换成集合RACSequence,array.rac_seuqence
// 2、把集合RACSequence转换RACSignal信号类,array.rac_sequence.signal
// 3、订阅信号,激活信号,会自动把集合中的所有值,遍历出来
[array.rac_sequence.signalsubscribeNext:^(idx) {
NSLog(@"%@",x);
}];
// 2、遍历字典,遍历出来的键值会包装成RACTuple(元祖对象)
NSDictionary *dic = @{@"name":@"wangtongke",@"age":@18};
[dic.rac_sequence.signal subscribeNext:^(id x) {
// 遍历字典 X为RAC的元祖(RACTuple)
// 解包元祖,会把元祖的值,按顺序给参数里边的变量赋值
RACTupleUnpack(NSString *key,NSString *value) = x;
// 以上 相当于一下写法
// NSString *key1 = x[0];
// NSString *value1 = x[1];
NSLog(@"%@ %@\n",key,value);
}];
/**
1、创建命令 initWithSignalblock:(RACSignal * (^)(id input))signalBlock
2、在signalBlock中,创建RACSignal,并且作为signalBlock的返回值
3、执行命令 -(RACSignal *)execute:(id)input
// 注意事项
1、signalBlock必须要返回一个signal,不能返回nil,
2、如果不想要传递信号,直接创建空的信号返回[RACSignal empty];
3、RACCommand,如果数据传递完毕,必须调用[subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中.
4、RACComand需要被强引用,否则接手不到RACCommand中的信号,因此,RACCommand中的信号是延迟发送的。
// 设计思想 : 内部signalBlock为什么要返回一直信号,这个信号有什么用
1、在RAC开发中,通常会把网络请求封装到RACCommand,直接执行某个RACCommand就能发送请求。
2、当RACCommand内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock返回的信号传递了
// 如何拿到RACCommand中返回信号发出的数据
1、RACCommand有个执行信号源executionSignal,这个signal of signal(信号的信号),意思是发出的数据是信号,不是普通的类型
2、订阅executionSignal就能拿到RACCommand中返回的信号,然后订阅signalblock返回的信号。
// 监听当前命令是否正在执行executing
// 使用场景 按钮点击,网络请求
*/
// ******* 1、创建命令
RACCommand*command = [[RACCommandalloc]initWithSignalBlock:^RACSignal*(idinput) {
NSLog(@"执行命令");
NSLog(@"input---%@",input);
// 创建空信号,必须返回信号
// return [RACSignal empty];
// ******* 2、创建信号,用来传递数据
return [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscribersendNext:@"请求数据"];
// 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕
[subscribersendCompleted];
return [RACDisposable disposableWithBlock:^{
}];
}];
}];
// 强引用命令,不然会自动销毁,接受不到数据
_command = command;
// ******* 3、订阅RACCommand中的信号
[command.executionSignalssubscribeNext:^(idx) {
[xsubscribeNext:^(idx) {
NSLog(@"%@",x);
}];
}];
// 高级用法
// switchToLatest:用于signal of signals,获取signal of signals 发出的最新的信号,也就是可以直接拿到RACCommand中的信号
[command.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"高级--%@",x);
}];
// ******** 4、监听命令是否执行完毕,默认会来一次,可以直接跳过,skip表示跳过第一次信号
[[command.executingskip:1]subscribeNext:^(idx) {
if([xboolValue] ==YES)
{
// 正在执行
NSLog(@"正在执行");
}
else
{
// 执行完成
NSLog(@"执行完成");
}
}];
// ******** 5、执行命令
[self.commandexecute:@1];
// RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。
RAC(self.label,text) = _textField.rac_textSignal;
// RACObserve(self, name):监听某个对象的某个属性,返回的是信号
[RACObserve(self.label,text)subscribeNext:^(idx) {
NSLog(@"%@",x);
}];
网友评论