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,默认是NO6.initWithSignalBlock:(RACSignal * (^)(id input))signalBlock:初始化RACCommand,参数为返回一个信号的block,即block返回的是executionSignals7.- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock:第一个参数设置当前command是否可用,第二个是执行的blockenableed默认是yes,所以第二个参数也可以为nil8.execute:(id)input:调用commandinputexecutionSignals的订阅者发送的值

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组件使用
-

1 0
原创粉丝点击