美文网首页MVVM. RAC
ReactiveCocoa----ReactiveObjc

ReactiveCocoa----ReactiveObjc

作者: CrazySnow | 来源:发表于2021-01-21 15:29 被阅读0次

    RactiveCocoa,是 Github 的一个开源框架,能够帮我们提供大量方便的事件处理方案,让我们更简单粗暴地去处理事件,现在分为 ReactiveObjCReactiveSwift,两个框架的功能使用相似,本文主要是针对ReactiveObjC做一个学习记录。

    RAC虽然最大的优点是提供了一个单一的、统一的方法去处理异步的行为,包括delegate方法,blocks回调target-action机制,notificationsKVO.

    RACSignal的使用及底层实现

    1、创建信号:把didSubscribe(代码块)保存到信号中

    2、订阅信号:调用signal的subscribeNext:(void (^)(id x))nextBlock

    3、调用RACDynamicSignaldidSubscribe把订阅者传递过去

    4、[subscriber sendNext:@1];调用订阅者的发送消息的方法,发送消息

    5、执行nextBlock这个代码块

    RACSignal

    RACSignal是信号类,当被订阅后,该信号会传递改变的数据,可以传递一下三种状态

    • sendNext(id):传递正确的数据,告诉订阅者下一步要干的事情
    • sendError:传递数据错误,告诉订阅者错误
    • sendCompleted:告诉订阅者发送完成,信号又变成冷信号了
    • RACDisposable对象,这个的作用就是,可以用来手动移除订阅
    • subscribeOn指定订阅后立即执行的block在哪个线程中执行
    • deliveOn指定subscribeNext在指定线程中执行
    - (void)useSingle{
        //创建信号并发送消息
       RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"将消息发送给订阅着"];
            [subscriber sendError:[NSError errorWithDomain:@"给订阅者发送错误信息" code:1001 userInfo:nil]];
            [subscriber sendCompleted];//告诉订阅者已经发送完成,跟sendError不能同时使用
            return nil;//需要手动移除订阅的话这里就返回RACDisposable类型的对象
           return [RACDisposable disposableWithBlock:^{
               //需要手动移除的时候使用这个return
           }];
        }];
        //创建订阅者
        RACDisposable *disposable = [signal subscribeNext:^(id x) {
            //处理发送过来的消息
        }error:^(NSError *error) {
            //错误
        }completed:^{
            //完成
        }];
        [disposable dispose];//手动移除订阅
    }
    

    总结:

    • createSignal的block在创建的时候是冷信号不会掉用,当订阅者subscribeNext之后才会把冷信号变成热信号,从而执行createSignal的block的block
    • sendNext执行完之后才会调用subscribeNext 的block

    RACSubject

    RACSubject 继承自RACSignal,是热信号,既可以充当信号也可以发送信号,并不确定什么时候终止

        // 1.创建信号
        RACSubject *subject = [RACSubject subject];
        
        // 2.订阅信号
        [subject subscribeNext:^(id  _Nullable x) {
            NSLog(@"x = %@", x);
        }];
        
        // 3.发送数据
        [subject sendNext:@10];
    

    总结:

    • 创建subject的时候,内部会创建一个数组subscribers用来存放所有的订阅者
    • 订阅信息的时候会创建订阅者,并保存到数组中
    • 会根据订阅者存入subscribers的顺序依次发送消息
    • RACSubject可以被多次订阅,并且只能是先订阅后发布,因为订阅者收不到订阅之前的消息

    RACReplaySubject

    RACReplaySubject旨在解决RACSubject不能先发送消息后订阅的问题

        // 1.创建信号
        RACReplaySubject *replaySubject = [RACReplaySubject subject];
          // 2.发送数据
        [replaySubject sendNext:@10];
        // 3.订阅信号
        [replaySubject subscribeNext:^(id  _Nullable x) {
            NSLog(@"x = %@", x);
        }];
       
    

    实现原理:

    • RACReplaySubject继承自RACSubject,在创建对象的时候,在父类的基础上会创建一个数组保存要发送的数据,且在subscribers中的订阅者全部都发送数据之后,就会删除当前要发送的数据
    • 订阅者订阅时才会发送数据
    • 订阅信号之后,先遍历一次保存数据的数组,然后发送数据

    RACCommand

    主要用于网络请求或者UI交互后立即执行的流程(eg:textfield输入内容后处理

    初始化方法

    • - (instancetype)initWithSignalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
    • - (instancetype)initWithEnabled:(nullable RACSignal<NSNumber *> *)enabledSignal signalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
      执行方法
    • - (RACSignal<ValueType> *)execute:(nullable InputType)input;
    - (void)RACCommandTest {
        
        // 1. 创建命令 - initWithSignalBlock
        RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
            
            NSLog(@"input:%@",input);
            // 注意: 不能返回空的信号
            return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                
                [subscriber sendNext:@"input命令来了, 开始发送数据"];
                return nil;
            }];
        }];
        
        // 2. 执行命令
        RACSignal *signal = [command execute:@"发送input 命令, 执行command"];
        
        // 3. 订阅信号
        [signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"数据: %@",x);
        }];
    }
    

    RACCommand属性后期添加

    RAC---rac_liftselector

    这个方法的主要作用就是保证多个信号都返回数据才会执行对应的方法(调用多个接口,拿到所有的返回值后刷新UI

        RACSignal *firstSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"第一次获取到的网络数据"];
            return nil;
        }];
        RACSignal *secondSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"第二次获取到的网络数据"];
            return nil;
        }];
    //updateUIWithData1:Data2:在所有信号都返回数据后执行
    //参数data1,data2必须与firstSignal, secondSignal相对应
        [self rac_liftSelector:@selector(updateUIWithData1:Data2:) withSignalsFromArray:@[firstSignal, secondSignal]];
    
    

    RACMulticastConnection

    该方法旨在解决,信号被多次订阅,信号的block就会多次执行的问题

        // RACMulticastConnection 其实是一个连接类,可以实现不管订阅多少次信号,信号的block 都只请求一次
        RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
           
            NSLog(@"请求数据");
            [subscriber sendNext:@"数据是回来啦"];
            return  nil;
        }];
    
        // 将信号转成连接类
        RACMulticastConnection *connection = [signalA publish];
        // 订阅连接类信号
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"第一次连接类信号订阅=%@", x);
        }];
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"第二次连接类信号订阅=%@", x);
        }];
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"第三次连接类信号订阅=%@", x);
        }];
        // 连接
        [connection connect];
    

    RAC创建一个定时器

    该部分主要介绍三个API:

    interval: 每次执行之前等待的时间.
    scheduler: 定时器执行所在的线程
    leeway: 指的是一个期望的容忍时间,将它设置为1秒,意味着系统有可能在定时器时间到达的前1秒或者后1秒才真正触发定时器.在调用时推荐设置一个合理的 leeway 值,需要注意,就算指定 leeway 值为 0,系统也无法保证完全精确的触发时间,只是会尽可能满足这个需求.

    • [[RACScheduler mainThreadScheduler] afterDelay:@"延时的秒数" schedule:^{ //延时后执行的代码块 }];//延时函数
    • [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) { }];//定时器,每隔多少时间做一次
      • [RACSignal interval:<#(NSTimeInterval)#> onScheduler:<#(RACScheduler *)#>]

      • [RACScheduler mainThreadScheduler]//主线程

      • [RACScheduler schedulerWithPriority:(RACSchedulerPriorityHigh) name:@"线程名"]开辟一个子线程

      • subscribeNext:<#^(id x)nextBlock#>//signal订阅信号

    • RAC(label, text) = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] map:^NSString *(NSDate * date) { return date.description; }];//秒表,利用map映射方法将原信号中的内容映射成新的指定内容。

    map映射的理解
    RAC中有
    RAC中包含两种映射方法mapflattenMap,映射方法是将原信号中的内容映射成新的指定内容。map的实现方法中可以看出是基于flattenMap方法的一层封装

    map:将会创建一个和原来一模一样的信号,只不过新的信号传递的值变成了block(value)
    flattenMap:把原信号的内容映射成一个新信号,并return返回给一个RACStream类型数据。实际上是根据前一个信号传递进来的参数重新建立了一个信号,这个参数,可能会在创建信号的时候用到,也有可能用不到。

        [[textField.rac_textSignal map:^id(id value) {
          //在这里对输入的内容做处理,然后将数据返回
            return [NSString stringWithFormat:@"map处理后的数据%@",value];
        }]subscribeNext:^(id x) {
            NSLog(@"%@",x);
        }];
    

    RACScheduler

    RACScheduler就是多线程的RAC封装,底层是GCD

    • currentScheduler//获取当前线程
    • mainThreadScheduler//获取主线程
    • scheduler//子线程,异步的
    • immediateScheduler//立即执行的线程,在主线程中执行的

    ** RACScheduler的优先级**

    • +(RACScheduler *)scheduler;//开辟子线程,默认优先级和名字
    • +(RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority;//设置优先级,默认名字
    • +(RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name;//设置名字和优先级

    RAC--TextField相关的API

    • [[textField rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(id x) {//x==textfield内输入的内容 }];
    • [textField.rac_textSignal subscribeNext:^(NSString *x) { }];
      以上两个API可以监听textfield输入文字变化

    RAC-- 添加手势

    • [[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) { //手势执行的代码块 }];
        UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
        [[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
            
        }];
        [self.view addGestureRecognizer:tap];
    

    RAC-- Button

    • [ [[button rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(UIButton * btn) { btn.selected = !btn.selected; }];//button点击事件
    • RAC(button, backgroundColor) = [RACObserve(button, selected) map:^UIColor *(NSNumber * selected) { return [selected boolValue] ? [UIColor redColor] : [UIColor greenColor]; }];//利用KVO宏定义检测按钮的选中状态并修改背景色,button的image同理可做

    RAC-- KVO及宏定义

    RACObserve宏是KVO在RAC中的实现形式

    • [RACObserve(scrollView, contentOffset) subscribeNext:^(id x) { }];//监控scrolleView的偏移量
    • RACObserve(button, selected)subscribeNext:^(id x) { }];//监控button的选中状态
    • RAC(button, backgroundColor)//用于给某个对象的某个属性绑定。通过RAC宏设置button的背景色,当按钮状态改变时会动态切换,包括图片、文字等属性的修改
      元组就是OC中的数组
    • RACTuplePack:把数据包装成RACTuple(元组类
    • RACTupleUnpack:把RACTuple(元组类)解包成对应的数据。

    RAC-- 代理

    对于系统view,可以使用这种方式代替
    自定义view,建议直接用RACSubject

    #pragma mark 代理
    - (void)delegateTest
    {
        UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"RAC" message:@"ReactiveCocoa" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ensure", nil];
        
        [[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * tuple) {
            
            LxDBAnyVar(tuple);
            LxDBAnyVar(tuple.first);
            LxDBAnyVar(tuple.second);
            LxDBAnyVar(tuple.third);
        }];
        [alertView show];
        
        
        //  更简单的方式:
        [[alertView rac_buttonClickedSignal]subscribeNext:^(id x) {
            
            LxDBAnyVar(x);
        }];
    
    }
    

    RAC-- Notification

    • [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"通知名" object:nil] subscribeNext:^(NSNotification * notification) { }];//RAC的通知要注意有些情况需要自己管理观察者,具体可以看下面这个大佬写的
      https://blog.csdn.net/jianghui12138/article/details/82466412

    相关文章

      网友评论

        本文标题:ReactiveCocoa----ReactiveObjc

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