ReactiveCocoa 学习笔记十六(RACCommandSupport)

RACCommandSupport

在 ReactiveCocoa 框架中,RACCommand 类可以将信号流作为信号量进行接收,从而可以封装一系列处理不同任务的信号流。为了扩展该类的用法,框架为下面几个类声明了 RACCommandSupport 分类。

  • UIBarButtonItem
  • UIButton
  • UIRefreshControl

这三个类所扩展的分类中都声明了一个属性,并实现了设置和获取方法。

@property (nonatomic, strong, nullable) RACCommand<__kindof UIRefreshControl *, id> *rac_command;

UIBarButtonItem (RACCommandSupport)UIButton (RACCommandSupport) 的实现方式类似,在调用下面的设置方法时,都会将控件的可用性绑定到命令的可用性上。

- (void)setRac_command:(RACCommand *)command {
	···
	disposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self];
	···
}

除此之外,该设置方法还会将控件的事件响应方法改变为命令的执行方法。

- (void)rac_commandPerformAction:(id)sender {
	[self.rac_command execute:sender];
}

所以,可以将按钮点击任务封装在 RACCommand 命令中,如下例子:

- (void)test 
{
    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(UIButton *  _Nullable input) {
        input.tag++;
        RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:[NSString stringWithFormat:@"signal1 send a next signal and input is %@",input]];
            return nil;
        }];
        
        RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:[NSString stringWithFormat:@"signal2 send a next signal and input is %@",input]];
            return nil;
        }];
        
        return input.tag%2 == 1 ? signal1 : signal2;
    }];
    
    command.allowsConcurrentExecution = YES;
    
    self.testBtn.rac_command = command;
    
    [command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
}

如此,便可以通过一个值,如这里的 input.tag ,来执行不同的任务。

运行结果:

2018-11-02 15:44:52.776475+0800 testP[4507:556667] signal1 send a next signal and input is <UIButton: 0x7fee1900afe0; frame = (0 0; 100 100); opaque = NO; tag = 1; layer = <CALayer: 0x600000f39100>>
2018-11-02 15:44:53.338504+0800 testP[4507:556667] signal2 send a next signal and input is <UIButton: 0x7fee1900afe0; frame = (0 0; 100 100); opaque = NO; tag = 2; layer = <CALayer: 0x600000f39100>>

UIRefreshControl (RACCommandSupport) 分类中设置该属性的实现方式与上面两个分类有所不同,为便于理解,将代码拆分如下:

- (void)setRac_command:(RACCommand *)command {
    objc_setAssociatedObject(self, UIRefreshControlRACCommandKey, command, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [objc_getAssociatedObject(self, UIRefreshControlDisposableKey) dispose];
    
    if (command == nil) return;
    
    RACDisposable *enabledDisposable = [command.enabled setKeyPath:@keypath(self.enabled) onObject:self];
    
    RACSignal *signal1 = [self rac_signalForControlEvents:UIControlEventValueChanged];
    RACSignal *signal2 = [signal1 map:^(UIRefreshControl *x) {
        
        RACSignal *signalInner1 = [command execute:x];
        RACSignal *signalInner2 = [signalInner1 catchTo:[RACSignal empty]];
        RACSignal *signalInner3 = [signalInner2 then:^{
                                      return [RACSignal return:x];
                                  }];
        return signalInner3;
    }];
                          
    RACSignal *signal3 = [signal2 concat];
    RACSignal *signal4 = [signal3 deliverOnMainThread];
    
    RACDisposable *executionDisposable = [signal4 subscribeNext:^(UIRefreshControl *x) {
        [x endRefreshing];
    }];
    
    RACDisposable *commandDisposable = [RACCompoundDisposable compoundDisposableWithDisposables:@[ enabledDisposable, executionDisposable ]];
    objc_setAssociatedObject(self, UIRefreshControlDisposableKey, commandDisposable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

该方法的实现步骤如下:

  1. 使用 UIRefreshControlRACCommandKey 将提供的参数 command 绑定到当前控件,完成属性赋值。

  2. 使用 UIRefreshControlDisposableKey 处理上个命令的相关释放操作。

  3. 判断 command 参数,如果为 nil ,则直接返回,否则继续执行。

  4. 将控件的 enabled 属性与 command 的 enabled 属性相绑定,即如控件不可用,那么命令不可执行。

  5. 为控件绑定 UIControlEventValueChanged 事件,得到 signal1 信号流,当事件被触发后,该信号流将当前控件作为信号量进行传递。

  6. 将接收到的控件信号量进行映射,映射为一个信号流类型的信号量,在信号流 signal2 中传递,在映射之前会先执行命令。

     1)当 signal1 中传递一个信号量时,命令 command 将其当作参数执行命令方法,
     	返回 signalInner1 信号流。
     
     2)signalInner2 信号流传递的信号量同信号流 signalInner1 一样,
     	只是并不会传递错误信号量。
     
     3)当 signalInner2 信号流结束后,即 signalInner1 发送了结束信号量,
     	signalInner3 信号流便会传递最初 signal1 传递的控件信号量。
     
     4)返回最后的映射值 signalInner3 信号流。
    
  7. 接着,signal2 会拼接自身传递的信号量(即信号流 signalInner3),从而得到新的信号流 signal3 。

  8. 保证 signal3 的信号量在主线程进行传递,得到 signal4 。

  9. 订阅 signal4 信号量,接收到控件信号量时,便停止该控件的刷新。

  10. 设置释放操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值