ReactiveCocoa学习篇——【上】

作者: 海强_hq | 来源:发表于2015-10-20 09:29 被阅读2348次

    1.ReactiveCocoa是什么?
    ReactiveCocoa(简称为RAC),是由Github开源的一个应用于iOS和OS开发的新框架,Cocoa是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。
    关键词:ReactiveCocoa是Github开源的一个框架

    2.ReactiveCocoa有什么用?
    在iOS开发过程中,经常会响应某些事件来处理某些业务逻辑,例如按钮的点击,上下拉刷新,网络请求,属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation)。但是这些事件都用不同的方式来处理,比如action、delegate、KVO、callback等。

    其实这些事件,都可以通过RAC处理,ReactiveCocoa为事件提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高聚合,低耦合的思想。
    关键词:在iOS日常开发过程中使用的一些处理事件的方法,RAC也能处理并且更方便

    3.已知的编程思想都有哪些?

    • 面向过程
      • 处理事情以过程为核心,一步一步的实现。
    • 面向对象
      • 万物皆对象
    • 链式编程思想
      • 是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。
    • 响应式编程思想
      • 不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。
    • 函数式编程思想
      • 是把操作尽量写成一系列嵌套的函数或者方法调用。
    • 函数响应式编程(FRP)
      • 不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。

    4.如何导入ReactiveCocoa框架?

    • 这里使用CocoaPods来导入
    • 创建一个新的工程演示下框架的导入
    • 打开终端,进入工程文件的同级目录
    • 新建一个profile文件,输入一下内容并保存
    use_frameworks!
    pod 'ReactiveCocoa', '~> 4.0.2-alpha-1'
    
    • 在终端输入指令pod install完成安装

    5.ReactiveCocoa常见类有哪些?

    第1种: RACSignal 信号类

    操作步骤:

    • 创建信号(冷信号)
    • 订阅信号(热信号)
    • 发送数据
        // 1.创建信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // block什么时候调用:当信号被订阅的时候就会调用
        // block作用:在这里面传递数据出去
            
            // 3.发送数据
            [subscriber sendNext:@1]; 
            return nil;
        }];
        
        // 2.订阅信号
        // x:信号传递出来的数据
        [signal subscribeNext:^(id x) {
        // block什么时候调用:当信号内部,发送数据的时候,就会调用,并且会把值传递给你
        // block作用:在这个block中处理数据
            
            NSLog(@"%@",x);
        }];
    

    RACSignal:表示有数据传递的时候
    RACDisposable:取消订阅信号
    RACSubscriber:订阅者,发送数据

    /*
    执行流程:
    1.创建信号RACDynamicSignal
     * 1.1 把didSubscribe保存到RACDynamicSignal
    2.订阅信号
     *  2.1 创建订阅者,把nextBlock保存到订阅者里面去
     *  2.2 就会调用信号的didSubscribe
    3.执行didSubscribe
     *  3.1 拿到订阅者发送订阅者
     * [subscriber sendNext:@1]内部就是拿到订阅者的nextBlock
     * 信号被订阅,就会执行创建信号时didSubscribe
     * 订阅者发送信号,就是调用nextBlock
     */
    

    第2种: RACDisposable 取消订阅或者清理资源

    操作步骤:

    • 创建信号
    • 订阅信号
    • 发送信号
        // 只要订阅者一直在,表示需要一直订阅信号,信号不会自动被取消订阅
        // 1.创建信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            
            _subscriber = subscriber;
            
            // 3.发送信号
            [subscriber sendNext:@1];
            
            return [RACDisposable disposableWithBlock:^{
           // 当信号取消订阅的时候就会调用
                NSLog(@"信号被取消订阅");
            }];
        }];
    
        // 2.订阅信号
        RACDisposable *disposable = [signal subscribeNext:^(id x) {
           
            NSLog(@"%@",x);
            
        }];
        
        
        // 取消订阅(主动取消)
        [disposable dispose];
        
    }
    

    第3种:RACSubject 信号提供者

    信号提供者:既可以充当信号,也可以充当订阅者,发送数据 一个信号允许被多次订阅

    操作步骤:

    • 创建信号
    • 订阅信号
    • 发送信号
    
        // 1.创建信号
        RACSubject *subject = [RACSubject subject];
        
        // 2.订阅信号
        [subject subscribeNext:^(id x) {
            
            NSLog(@"订阅者一%@",x);
        }];
        
        [subject subscribeNext:^(id x) {
            
            NSLog(@"订阅者二%@",x);
        }];
        
        
        // 3.发送信号
        [subject sendNext:@1];
    
    
    
    /*
     执行流程:
     1.创建信号
     2.订阅信号
     * 创建订阅者
     * [self subscribe:o]订阅信号,仅仅是把订阅者保存起来.
     3.发送信号
     * 把所有的订阅者遍历出来,一个一个的调用它们nextBlock
     */
    

    第4种: RACReplaySubject 重复提供信号类(RACSubject的子类)

    允许先发送信号,在订阅信号 重复信号提供者

    操作步骤:

    • 创建信号
    • 订阅信号
    • 发送信号
        // 1.创建信号
        RACReplaySubject *subject = [RACReplaySubject subject];
        
        // 2.发送信号
        [subject sendNext:@"123123"];
        [subject sendNext:@"345"];
        [subject sendNext:@"456456"];
        // 1.把值保存到数组
        // 2.遍历所有的订阅者,调用nextBlock
        
        // 3.订阅信号
        [subject subscribeNext:^(id x) {
            NSLog(@"订阅者一%@",x);
        }];
        // 1.遍历所有值,拿到订阅者去发送
        [subject subscribeNext:^(id x) {
            NSLog(@"订阅者二%@",x);
        }];
        
    
    /*
     执行流程:
     1.创建信号
     2.订阅信号
     * 创建订阅者,保存nextBlock保存
     * 遍历valuesReceived需要发送的所有值,拿到一个一个值,利用订阅者发送数据
     3.发送信号
     * 把发送的值,保存到数组
     * 把所有的订阅者遍历出来,一个一个的调用它们nextBlock
     */
    

    第5种:RACMulticastConnection

    用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理

    简单使用

     // 1.创建信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            
            NSLog(@"发送请求");
            
            // 3.发送信号
            [subscriber sendNext:@"网络数据"];
            
            return nil;
        }];
        
        // 2.把信号转换成连接类
        RACMulticastConnection *connect = [signal publish];
        
        // 3.订阅连接类的信号,注意:一定是订阅连接类的信号,不再是源信号
        [connect.signal subscribeNext:^(id x) {
           
            NSLog(@"%@",x);
            
        }];
        
        [connect.signal subscribeNext:^(id x) {
            
            NSLog(@"%@",x);
            
        }];
        [connect.signal subscribeNext:^(id x) {
            
            NSLog(@"%@",x);
            
        }];
        
        // 4.连接
        [connect connect];
    
    

    一个信号即使被订阅多次,也只是发送一次请求 RACMulticastConnection:用于信号中请求数据,避免多次请求数据

       /* 
         执行流程
            1.创建信号
            * 创建RACDynamicSignal,并且把didSubscribe保存到
            2.把信号转换成连接类
            * 创建信号提供者RACSubject
            * [self multicast:subject]:设置原始信号的多点传播subject,本质就是把subject设置为原始信号的订阅者
            * 创建RACMulticastConnection,把原始信号保存到_sourceSignal,把subject保存到_signal
            3.保存订阅者
            4.连接 [connect connect]
            * 订阅_sourceSignal,并且设置订阅者为subject
            5.执行didSubscribe
            6.[subject sendNext]遍历所有的订阅者发送信号
         */
    
    

    第6种:RACCommand 用于处理事件的类

    RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,它可以很方便的监控事件的执行过程

    • 使用场景: 监听按钮点击,网络请求
    • RACCommand使用步骤:创建命令 -> 执行命令
    • RACCommand使用注意点:内部不允许传入一个nil的信号
    // 1.创建命令
        RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
            // signalBlock调用时刻:只要命令一执行就会调用
            // signalBlock作用:处理事件
            NSLog(@"%@",input);
            
            
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
                
                    NSLog(@"didSubscribe");
                // didSubscribe调用时刻:执行命令过程中,就会调用
                // didSubscribe作用:传递数据
                
                // subscriber -> [RACReplaySubject subject]
                // RACReplaySubject:把值保存起来,遍历所有的订阅者发送这个值
                [subscriber sendNext:@1];
                
                return nil;
            }];
        }];
        
        // 2.执行命令
        RACReplaySubject *replaySubject = (RACReplaySubject *)[command execute:@"执行命令"];
        
        
        // 3.获取命令中产生的数据,订阅信号
        [replaySubject subscribeNext:^(id x) {
           
            NSLog(@"%@",x);
        }];
        // 1.创建订阅者,订阅信号
        // 2.遍历所有值,拿到订阅者去发送
    
        /* 
         执行流程:
        // 1.创建命令
            * 把signalBlock保存到命令中
         // 2.执行命令
            * 调用命令signalBlock
            * 创建多点传播连接类, 订阅源信号,并且设置源信号的订阅者为RACReplaySubject
            * 返回源信号的订阅者
        */
    

    switchToLatest用法

     // 1.创建了一个信号中的信号,只能发送信号
        RACSubject *signalOfSignals = [RACSubject subject];
        
        // 创建信号,发送普通数据
        RACSubject *signalA = [RACSubject subject];
        RACSubject *signalB = [RACSubject subject];
        
        // 2.通过订阅信号中信号,拿到signal发出值
        // 获取信号中的信号发送最新信号
        [signalOfSignals.switchToLatest subscribeNext:^(id x) {
            
            NSLog(@"%@",x);
        }];
        
        
        // 3.发送信号
        // 信号中的信号发送数据
        [signalOfSignals sendNext:signalA];
        [signalOfSignals sendNext:signalB];
        
        // 信号发送数据
        [signalB sendNext:@"发送普通数据"];
    
    
      // 1.创建命令
        RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
            // 1.命令执行的时候调用
            NSLog(@"执行SignalBlock");
            // 处理事情,网络请求
            
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
                // 2.执行完SignalBlock,接着就会调用
                NSLog(@"执行didSubscribe");
                // 传送数据,也只会来一次
                [subscriber sendNext:@"请求到网络数据"];
                
                // 一定要做事情,声明数据传递完成
                // 当数据传递完成,命令就执行完成
                [subscriber sendCompleted];
                
                return nil;
            }];
        }];
    
        // 直接获取命令中新发出信号
        // switchToLatest:只能用于signalOfSignals
        // 作用:就是拿到signalOfSignals发出的最新信号
        [command.executionSignals.switchToLatest subscribeNext:^(id x) {
            
            NSLog(@"%@",x);
        }];
        
        // 订阅当前命令执行状态的信号,正在执行,没有执行
        [command.executing subscribeNext:^(id x) {
            // x:YES 正在执行
            // x:No 没有执行/执行完成
            if ([x boolValue] == YES) {
                NSLog(@"正在执行");
            }else{
                NSLog(@"没有执行/执行完成");
            }
            
        }];
        
        // 2.执行命令
        RACSignal *signal = [command execute:@1];
    

    6.ReactiveCocoa常用集合类?

    RACTuple 和RACSequence 的使用

    RACTuple: 元组类,类似NSArray,用来包装值. RACSequence: RAC中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典

    简单使用

        RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"1",@1,@3,@5]];
        NSString *str = tuple[0];
    

    OC->RAC

        NSArray *arr = @[@"123",@1,@3,@5];
        
        // OC数组转换成RAC集合
        RACSequence *sequence = arr.rac_sequence;
        
        // 把集合转换成signal
        RACSignal *signal = sequence.signal;
        
        // 订阅集合类的信号,只要订阅这个信号,就会遍历集合中所有元素
        [signal subscribeNext:^(id x) {
            
            NSLog(@"%@",x);
        }];
        
        [arr.rac_sequence.signal subscribeNext:^(id x) {
            NSLog(@"%@",x);
        }];
    

    宏的使用

        NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
        
        [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
            //        id value = x[1];
            //        id key = x[0];
            
            // 宏的参数,存放需要生成的变量名
            // 宏会自动生成参数里面的变量
            RACTupleUnpack(id key,id value) = x;
            
            NSLog(@"%@ : %@",key,value);
        }];
    

    字典转模型
    方式一:

        // RAC:集合,相当于数组或者字典
        // 这个类的创建必须要依赖于数组或者字典
        // 解析plist
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
        NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
        _flags = [NSMutableArray array];
        
        [dictArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
            Flag *flag =  [Flag flagWithDict:x];
            [_flags addObject:flag];
        }];
    

    方式二:

        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
        NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
        _flags = [NSMutableArray array];
    
       _flags = [[dictArr.rac_sequence map:^id(id value) {
            // value:就是原始信号发送的值,字典
            // block返回值就是需要映射的对象
            return [Flag flagWithDict:value];
        }] array];
        NSLog(@"%@",_flags);
    

    相关文章

      网友评论

      • 丶雨凡:新建一个Podfile吧......
        某些新手会被误导的 :smile:
      • 我的大名叫小爱:没有 demo 感觉学起来有压力啊.
      • sadjason:也在学习ReactiveCocoa,看了不少资料,博主的学习套路比较贴合我的需求,感谢!

      本文标题:ReactiveCocoa学习篇——【上】

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