美文网首页
(七)、iOS RAC - RACCommand

(七)、iOS RAC - RACCommand

作者: Buddha_like | 来源:发表于2019-03-25 17:37 被阅读0次
    RACCommand 是 RAC 中的最复杂的一个类之一,它也是一种广义上的信号,RAC 中信号其实是一种对象(或者是不同代码块)之间通信机制,在面向对象中,类之间的通信方式主要是方法调用,而信号也是一种调用,只不过它是函数式的,因此信号不仅仅可以在对象之间相互调用(传参),也可以在不同代码块(block)之间进行调用。
    一般来说,RAC 中用 RACSignal 来代表信号。一个对象创建 RACSignal 信号,创建信号时会包含一个 block,这个 block 的作用是发送信号给订阅者(类似方法返回值或回调函数)。另一个对象(或同一个对象)可以用这个信号进行订阅,从而获得发送者发送的数据。这个过程和方法调用一样,信号相当于暴露给其它对象的方法,订阅者订阅信号相当于调用信号中的方法(block),只不过返回值的获得变成了通过 block 来获得。此外,你无法直接向 RACSignal 传递参数,要向信号传递参数,需要提供一个方法,将要传递的参数作为方法参数,创建一个信号,通过 block 的捕获局部变量方式将参数捕获到信号的 block 中。
    而 RACCommand 不同,RACCommand 的订阅不使用 subscribeNext 方法而是用 execute: 方法。而且 RACCommand 可以在订阅/执行(即 excute:方法)时传递参数。因此当需要向信号传递参数的时候,RACComand 更好用。
    此外,RACCommand 包含了一个 executionSignal 的信号,这个信号是对用户透明的,它是自动创建的,由 RACCommand 进行管理。许多资料中把它称之为信号中的信号,是因为这个信号会发送其它信号——即 RACCommand 在初始化的 signalBlock 中创建(return)的信号。这个信号是 RACCommand 创建时由我们创建的,一般是用于处理一些异步操作,比如网络请求等。
    先来看看RACCommand 基础写法
    /**
     Command翻译过来就是命令
    */
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
          NSLog(@"执行1");
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            NSLog(@"执行3");
            [subscriber sendNext:input];
            return nil;
        }];
    }];
    
    /**
     executionSignals就是用来发送信号的信号源,
     需要注意的是这个方法一定要在执行execute方法之前,否则就不起作用了,
     */
     [command.executionSignals subscribeNext:^(id  _Nullable x) {
        [x subscribeNext:^(id  _Nullable x) {
            NSLog(@"执行4");
            NSLog(@"接收数据%@",x);
        }];
        NSLog(@"执行2");
    }];
    [command execute:@"发送消息"];
    
    整体看下来,发现,它与单纯的RACSignal是有一些区别的,从代码上我们直观的可以发现,RACCommand 执行顺序
    1).RACCommand通过execute发送一个参数;
    2).这时候会执行 initWithSignalBlock块,这个内部将创建并返回一个信号,RACCommand 通过executionSignals(也是一个信号) 找到返回信号并发送信号
    3).信号内部进行发送消息.
    4).订阅信号,接受信息.
    

    通过这个特性,我们可以做一个简单的Demo。

    项目最终实现目的,视图上 一个按钮 一个TextView, 当我们点击这个按钮的时候,会刷新TextView上的文字.
    项目文件构成分成两个文件 一个ViewController控制器,另一个是ViewModel
    代码:
    ViewModel.h :
    
    typedef NS_ENUM(NSUInteger, HTTPRequestStatus) {
    HTTPRequestStatusBegin,
    HTTPRequestStatusEnd,
    HTTPRequestStatusError,
    };
    
    @interface RACCommandViewModel : NSObject
    
    @property (nonatomic, assign, readwrite) HTTPRequestStatus status;
    @property (nonatomic, strong, readwrite) RACCommand *requestData;
    @property (nonatomic, strong, readwrite, nullable) NSDictionary *data;
    @property (nonatomic, strong, readwrite, nullable) NSError *error;
    @end
    
    ViewModel.m :
    
    - (instancetype)init {
    self = [super init];
    if (self) {
        
        [self subscriberConmandSignals];
    }
    return self;
    }
     
    /**
     RACCommand 中封装了各种信号,我们只用到了外层信号(executionSignal)和内层信号。订阅这些信号能够让我们实现两个目的:拿到请求返回的数据、跟踪 RACCommand 开始结束状态。定义一个方法来做这些事情:
     */
    
    /**
     订阅外层信号(即 executionSignals)。外层信号在订阅或执行(即 execute: )时发送。因此我们可以将它视作请求即将开始之前的信号,在这里将 self.error 清空,将 requestStatus 修改为 begin。
     
     订阅内层信号,因为内层信号由外层信号(executionSignals)作为数据发送(sendNext:),而发送的数据一般是作为 subcribeNext:时的 block 的参数来接收的,因此在这个块中,块的参数就是内层信号。这样我们就可以订阅内层信号了,同时获取数据(保存到 data 属性)并修改 requestStatus 为 end。
     
     RACCommand 比较特殊的一点是 error 信号需要在 errors 中订阅,而不能在 executionSignals 中订阅。在这里我们订阅了 errors 信号,并修改 data、error 和 requestStatus 属性值。
     */
    - (void)subscriberConmandSignals {
        
        @weakify(self)
         //1. 订阅外层信号
        [self.requestData.executionSignals subscribeNext:^(RACSignal* innerSignal) {
            @strongify(self)
            // 2. 订阅内层信号
            [innerSignal subscribeNext:^(NSDictionary *x){
                self.data = x;
                self.status = HTTPRequestStatusEnd;
                NSLog(@"11111111");
            }];
            self.error = nil;
            self.status = HTTPRequestStatusBegin ;
        }];
        
         // 3. 订阅 errors 信号
        [self.requestData.errors subscribeNext:^(NSError *_Nullable x){
           @strongify(self)
            self.error = nil;
            self.data = nil;
            self.status = HTTPRequestStatusError;
        }];
    }
    
    /**
        懒加载方式来初始化 RACCommand 对象:
    */
    - (RACCommand *)requestData {
        if (!_requestData) {
            _requestData = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(NSString* input) {
                //NSDictionary *body = @{@"memberCode": input};
                // 进行网络操作,同时将这个操作封装成信号 return
                return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                    [subscriber sendNext:@"网络请求回来的数据"];
                    [subscriber sendCompleted];//告诉外界发送完了
                     return nil;
                }];
            }];
        }
        return _requestData;
    }
    
    接下来是控制器的代码:
    
    ViewController.m
    
    @interface RACCommandVC ()
    
    @property (nonatomic, strong, readwrite) RACCommandViewModel *viewModel;
    @property (nonatomic, strong, readwrite) UIButton *btn;
    @property (nonatomic, strong, readwrite) UITextView *textView;
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        _viewModel = [[RACCommandViewModel alloc] init];
        [self.view addSubview:self.btn];
        [self.view addSubview:self.textView];
        [self bindViewModel];
    }
    
    - (void)bindViewModel {
    
      @weakify(self)
         [[RACObserve(_viewModel, status) skip:1] subscribeNext:^(NSNumber* x){
            switch ([x intValue]) {
             case HTTPRequestStatusBegin:
                NSLog(@"开始刷新,展示菊花");
                 break;
             case HTTPRequestStatusEnd:
                NSLog(@"结束刷新,隐藏菊花");
                    break;
             case HTTPRequestStatusError:
                NSLog(@"数据错误");
                 break;
             default:
                break;
        }
    }];
    
         RAC(self.textView, text) = [[RACObserve(_viewModel, data) skip:1]map:^id _Nullable(NSString *value) {
             return value;
         }];
    
         [[_btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
          @strongify(self)
           [self.viewModel.requestData execute:@"96671e1a812e46dfa4264b9b39f3e225"];
        }];
    }
    
    - (UIButton *)btn {
        if (!_btn) {
            _btn = [UIButton buttonWithType:UIButtonTypeCustom];
            _btn.backgroundColor = [UIColor redColor];
            [_btn setTitleColor:[UIColor greenColor] forState:UIControlStateNormal];
            [_btn setTitle:@"点击刷新" forState:UIControlStateNormal];
            _btn.frame  = CGRectMake(0, 150, 200, 60);
        }
         return _btn;
    }
    
    - (UITextView *)textView {
        if (!_textView) {
            _textView = [[UITextView alloc] initWithFrame: CGRectMake(60,300,200,200)];
            _textView.backgroundColor = [UIColor greenColor];
        }
        return _textView;
    }
    

    看一下最终的效果图

    屏幕快照 2019-03-25 05.36.03 PM.png 屏幕快照 2019-03-25 05.36.13 PM.png 屏幕快照 2019-03-25 05.36.22 PM.png

    相关文章

      网友评论

          本文标题:(七)、iOS RAC - RACCommand

          本文链接:https://www.haomeiwen.com/subject/jzpbvqtx.html