RACCommand使用总结
来源:互联网 发布:网络预约车管理办法 编辑:程序博客网 时间:2024/06/08 05:12
1、RACCommand是什么
平常使用button可以使用rac_signalForControlEvents获得点击事件的信号,执行点击事件。rac提供了rac_command来执行某个block。首先看下RACCommand的基本属性有哪些:
1. executionSignals:需要执行的block成功的时候返回的信号,他是在主线程执行的。2. executing:判断当前的block是否在执行,执行完之后会返回@(NO).3. enabled:当前命令是否enabled,默认是no,他也可以根据enableSignal来设置或者allowsConcurrentExecution设置为NO的时候(command已经开始执行)4. errors:执行command的时候获取的error都会通过这个信号发送5. allowsConcurrentExecution:是否允许并发执行command,默认是NO。6.initWithSignalBlock:(RACSignal * (^)(id input))signalBlock:初始化RACCommand,参数为返回一个信号的block,即block返回的是executionSignals7.- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock:第一个参数设置当前command是否可用,第二个是执行的block。enableed默认是yes,所以第二个参数也可以为nil。8.execute:(id)input:调用command,input为executionSignals的订阅者发送的值
2.源码分析
通过以下代码可以看出_executionSignals是最新的activeExecutionSignals,可能是多个(允许并发执行设为yes的时候)。
RACSignal *newActiveExecutionSignals = [[[[[selfrac_valuesAndChangesForKeyPath:@keypath(self.activeExecutionSignals) options:NSKeyValueObservingOptionNew observer:nil] reduceEach:^(id _, NSDictionary *change) { NSArray *signals = change[NSKeyValueChangeNewKey]; if (signals == nil) return [RACSignal empty]; return [signals.rac_sequence signalWithScheduler:RACScheduler.immediateScheduler]; }] concat] publish] autoconnect]; _executionSignals = [[[newActiveExecutionSignals map:^(RACSignal *signal) { return [signal catchTo:[RACSignal empty]]; }] deliverOn:RACScheduler.mainThreadScheduler] setNameWithFormat:@"%@ -executionSignals", self];
executing,判断当前信号是否在执行,从下面可以看出,他实际是当前有多少个活跃信号的监听,当有活跃信号的是否就返回YES,否则返回no,并且默认是no。replayLast确保是最新的值,distinctUntilChanged当值发生改变的时候才会返回新值
RACSignal *immediateExecuting = [RACObserve(self, activeExecutionSignals) map:^(NSArray *activeSignals) { return @(activeSignals.count > 0); }]; _executing = [[[[[immediateExecuting deliverOn:RACScheduler.mainThreadScheduler] // This is useful before the first value arrives on the main thread. startWith:@NO] distinctUntilChanged] replayLast] setNameWithFormat:@"%@ -executing", self];
enabled是由enableSignal和是否允许执行两者结合,只有两者都为真的时候才能执行。enbaleSingnal是传入的值,那moreExecutionsAllowed是个什么呢?,他是实际是[RACSignal
if:RACObserve(self, allowsConcurrentExecution)
then:[RACSignal return:@YES]
else:[immediateExecuting not]]这句代码返回的。如果允许并发,enable会始终是yes,如果不允许则判断当前是否有可以立即执行的信号,有的话enable返回no,如果没有,则command当前是可以执行的,返回yes。
_immediateEnabled = [[RACSignal combineLatest:@[ enabledSignal, moreExecutionsAllowed ]] and]; _enabled = [[[[[self.immediateEnabled deliverOn:RACScheduler.mainThreadScheduler] startWith:@YES] distinctUntilChanged] replayLast] setNameWithFormat:@"%@ -enabled", self];
errors:当最新执行的信号发送错误信号时会把它赋值给errors,中间经过了connect
RACMulticastConnection *errorsConnection = [[[newActiveExecutionSignals flattenMap:^(RACSignal *signal) { return [[signal ignoreValues] catch:^(NSError *error) { return [RACSignal return:error]; }]; }] deliverOn:RACScheduler.mainThreadScheduler] publish]; _errors = [errorsConnection.signal setNameWithFormat:@"%@ -errors", self];
allowsConcurrentExecution:允许并发执行后,enable会始终返回yes,会在execute的时候用到,如果返回no,则不会执行block
BOOL enabled = [[self.immediateEnabled first] boolValue]; if (!enabled) { NSError *error = [NSError errorWithDomain:RACCommandErrorDomain code:RACCommandErrorNotEnabled userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"The command is disabled and cannot be executed", nil), RACUnderlyingCommandErrorKey: self }]; return [RACSignal error:error]; }
execute:
1.判断是否可以执行 BOOL enabled = [[self.immediateEnabled first] boolValue]; if (!enabled) { NSError *error = [NSError errorWithDomain:RACCommandErrorDomain code:RACCommandErrorNotEnabled userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(@"The command is disabled and cannot be executed", nil), RACUnderlyingCommandErrorKey: self }]; return [RACSignal error:error]; }2.获得block返回的信号 RACSignal *signal = self.signalBlock(input); NSCAssert(signal != nil, @"nil signal returned from signal block for value: %@", input);3.对signal进行连接 // We subscribe to the signal on the main thread so that it occurs _after_ // -addActiveExecutionSignal: completes below. // // This means that `executing` and `enabled` will send updated values before // the signal actually starts performing work. RACMulticastConnection *connection = [[signal subscribeOn:RACScheduler.mainThreadScheduler] multicast:[RACReplaySubject subject]]; @weakify(self);4.进行增加和移除操作,增加的是RACReplaySubject [self addActiveExecutionSignal:connection.signal]; [connection.signal subscribeError:^(NSError *error) { @strongify(self); [self removeActiveExecutionSignal:connection.signal]; } completed:^{ @strongify(self); [self removeActiveExecutionSignal:connection.signal]; }];5.确保sideEffect只执行一次,并且缓存数据 [connection connect]; return [connection.signal setNameWithFormat:@"%@ -execute: %@", self, [input rac_description]];
3.如何使用
简单的一个登录操作
1.创建ViewModel
@interface ViewModel : NSObject@property (nonatomic,copy) NSString *username;@property (nonatomic,copy) NSString *password;@property (nonatomic,readonly) RACCommand *loginCommand;@end@interface ViewModel ()@property (nonatomic,strong) RACCommand *loginCommand;@end@implementation ViewModel- (instancetype)init { self = [super init]; if (!self) { return nil; } @weakify(self) self.loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { @strongify(self) return [self loginWithUsername:self.username password:self.password]; }]; return self;}//有效性判断- (RACSignal *)validateLoginInputs { return [RACSignal combineLatest:@[RACObserve(self, username), RACObserve(self, password)] reduce:^id(NSString *username,NSString *password){ return @(username.length > 0 && password.length > 0); }];}//真正登录的执行代码- (RACSignal *)loginWithUsername:(NSString *)username password:(NSString *)password { return [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSLog(@"beigin login"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [subscriber sendNext:@"login"]; [subscriber sendCompleted]; NSLog(@"end login"); }); return nil; }] publish] autoconnect];}
2、绑定viewModle
#import "ViewController.h"#import <Masonry/Masonry.h>#import <ReactiveCocoa/ReactiveCocoa.h>#import <libextobjc/EXTScope.h>#import "ViewModel.h"@interface ViewController ()@property (nonatomic,strong) UITextField *usernameTextField;@property (nonatomic,strong) UITextField *passwordTextField;@property (nonatomic,strong) UIButton *button;@property (nonatomic,strong) ViewModel *viewModel;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. 1.view配置 self.viewModel = [[ViewModel alloc] init]; self.usernameTextField = [[UITextField alloc] init]; self.usernameTextField.placeholder = @"输入用户名"; self.usernameTextField.backgroundColor = [UIColor purpleColor]; self.passwordTextField = [[UITextField alloc] init]; self.passwordTextField.placeholder = @"输入密码"; self.passwordTextField.backgroundColor = [UIColor purpleColor]; UIView *superView = self.view; [self.view addSubview:self.usernameTextField]; [self.view addSubview:self.passwordTextField]; @weakify(self) [self.usernameTextField mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(superView.mas_centerX); make.centerY.equalTo(@(10)); make.width.equalTo(@(200)); make.height.equalTo(@(35)); }]; [self.passwordTextField mas_makeConstraints:^(MASConstraintMaker *make) { make.width.mas_equalTo(self.usernameTextField.mas_width); make.height.mas_equalTo(self.usernameTextField.mas_height); make.centerY.mas_equalTo(self.usernameTextField.mas_centerY).offset(45); make.centerX.mas_equalTo(self.usernameTextField.mas_centerX); }]; self.button = [UIButton buttonWithType:UIButtonTypeCustom]; [self.view addSubview:self.button]; [self.button mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.view.mas_centerX); make.centerY.equalTo(self.view.mas_bottom).offset(-40); make.width.mas_equalTo(200); make.height.mas_equalTo(30); }]; [self.button setTitle:@"登陆" forState:UIControlStateNormal]; self.button.backgroundColor = [UIColor redColor]; 2、绑定viewModel [self.usernameTextField.rac_textSignal subscribeNext:^(id x) { @strongify(self) self.viewModel.username = x; }]; [[self.passwordTextField rac_textSignal] subscribeNext:^(id x) { @strongify(self) self.viewModel.password = x; }]; self.button.rac_command = self.viewModel.loginCommand; //判断是否正在执行 [self.button.rac_command.executing subscribeNext:^(id x) { if ([x boolValue]) { NSLog(@"login.."); } else { NSLog(@"end logining"); } }]; //执行结果 [self.button.rac_command.executionSignals.flatten subscribeNext:^(id x) { NSLog(@"result:%@",x); }]; //错误处理 [self.button.rac_command.errors subscribeNext:^(id x) { NSLog(@"error:%@",x); }];}- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.view endEditing:YES];}@end
3、以上是非常简单的viewModel和command使用例子。下面是其他的较为复杂的例子:command组件使用
-
- RACCommand使用总结
- RACCommand使用简介
- ReactiveCocoa之RACCommand使用(五)
- ReactiveCocoa之RACCommand使用(五)
- ReactiveCocoa:理解和使用RACCommand
- ReactiveCocoa要点:理解和使用RACCommand
- ReactiveCocoa基本组件:理解和使用RACCommand
- ReactiveCocoa基本组件:理解和使用RACCommand
- ReactiveCocoa基本组件:理解和使用RACCommand
- ReactiveCocoa基本组件:理解和使用RACCommand
- UIButton使用RACCommand后setEnable方法失效的问题
- RACCommand 粗解
- #关于RACCommand的思考
- ReactiveCocoa基本组件:深入浅出RACCommand
- RACCommand 有sendError对executionSignals 怎么破
- ReactiveCocoa Essentials: Understanding and Using RACCommand
- RAC学习基础应用(四)--RACCommand
- 使用总结
- Java虚拟机(5)内存区域和GC机制
- 蓝懿教育 记录微信打飞机小游戏
- VBS中Sendkey键盘对应的码表
- Flex中用RemoteObject与java后台通信
- 多重部分和问题
- RACCommand使用总结
- qt 与pencv 在linux下的环境搭建(一:qt平台的搭建)
- 使用淘宝中间件cobar实现mysql分库分表
- 研发类笔试题基础(第一篇
- PAT Advanced 1031
- [11]PHP_W3Cschool中的PHP测试题题目以及答案
- 责任链模式应用
- vmstat si so单位
- DuiLib : 模拟百度网页的填写和搜索