美文网首页
ReactiveCocoa系列之一:基础

ReactiveCocoa系列之一:基础

作者: 十拿九稳啦 | 来源:发表于2018-11-17 15:45 被阅读0次

    我们在开发中会遇到什么问题?

    1. 基于Cocoa框架的代码开发比较集中于用户事件和应用程序状态改变的处理上。UI事件,网络回调,数据源,代理,通知等这些实际业务处理需要用到的api都不相同。业务增长,这些就会越来越多,代码会变得越来越繁杂,不好维护。

    2. 关注一个对象属性的变化,原生的方式有重写setter和KVO。重写setter需要将事件从对象内部抛出来,系统类还需要去继承实现,很不优雅。而KVO的诟病我想大家都懂,所有的属性都在一个方法里,还需要记得移除观察者。

    3. 业务上有依赖关系的事务,一般使用函数多层调用或者block回调(鲁莽地把多线程操作也归为此类)来建立依赖。PS这里只关注表面写法;一旦依赖层级较多,代码就不能非常直观地展示业务依赖关系;

    如下发薪资的业务,用线程依赖的方式来处理,就不是很直观。RAC地写法后面讲。


    发薪资流程图
        NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
    
        NSBlockOperation *attendanceOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"考勤");
        }];
        
        NSBlockOperation *workOvertimeOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"加班");
        }];
        
        NSBlockOperation *otherOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"其它补贴");
        }];
        
        NSBlockOperation *HROperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"HR确认");
        }];
        
        NSBlockOperation *employeeOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"员工确认");
        }];
        
        NSBlockOperation *wageOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"发工资");
            NSLog(@"------------------------------------");
        }];
        
        [workOvertimeOperation addDependency:attendanceOperation];
        [HROperation addDependency:attendanceOperation];
        [HROperation addDependency:workOvertimeOperation];
        [HROperation addDependency:otherOperation];
        
        [employeeOperation addDependency:attendanceOperation];
        [employeeOperation addDependency:workOvertimeOperation];
        [employeeOperation addDependency:otherOperation];
        
        [wageOperation addDependency:HROperation];
        [backgroundQueue addOperation:employeeOperation];
        [backgroundQueue addOperation:HROperation];
        [wageOperation addDependency:employeeOperation];
        [backgroundQueue addOperation:otherOperation];
        [backgroundQueue addOperation:workOvertimeOperation];
        [backgroundQueue addOperation:wageOperation];
        [backgroundQueue addOperation:attendanceOperation];
    

    ReactiveCocoa如何解决或者优化这些问题?

    我们先来看看RAC对于上述问题点的对应解决方案。
    RAC 提供了统一的事件处理API,让事件的绑定和实事件处理的代码可以放到一起实现 高聚合

    • 文本框文字改变
    [textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
        //改变后的处理代码
    }];
    
    • 代替点击/手势事件
    //按钮事件
    [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        //按钮事件处理代码
    }];
    
    //手势事件
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
    [[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
        //手势处理代码
    }];
    [view addGestureRecognizer:tap];
    
    • 替代代理
    // 需求:将自定义View中的按钮点击事件`buttonAction`抛出
    [[customView rac_signalForSelector:@selector(buttonAction:)] subscribeNext:^(UIButton *x) {
        //事件响应代码
    }];
    
    • 替代KVO
    // 监听tableView的contentOffset变化
    [[customView rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld observer:self] subscribeNext:^(RACTwoTuple<id,NSDictionary *> * _Nullable tuple) {
         //值变化的处理代码
    }];
    
    • 替代通知的监听
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
        //收到通知后的处理代码  
    }];
    

    ReactiveCocoa解决方案的设计理念

    为了更好地理解RAC解决问题的方式,我们先来了解一下RAC是怎么设计的。
    ReactiveCocoa(简称RAC)是最初由GitHub团队开发的一套基于Cocoa的FRP框架。FRP即Functional Reactive Programming(函数式响应式编程),也可以说是 响应式编程的函数式实现

    接下来对函数式编程和响应式编程做个简单了解

    面向对象强调的是将与某种数据类型相关的一系列操作都封装到该数据类型中去,因此,在数据类型中难免存在大量状态,以及相关的行为。虽然这很符合人类的逻辑直觉,但是当类型关系变得错综复杂时,类型中状态的改变和类型之间彼此的继承和依赖将使程序的复杂度呈几何级数上升。
    避免使用程序状态和可变对象,是降低程序复杂度的有效方式之一,而这也是函数式编程的精髓。函数式编程强调执行的结果,而非执行的过程。
    我们先构建一系列简单却具有一定功能的小函数,然后再将这些函数进行组装以实现它完整的逻辑和复杂的运算,这是函数式编程的基本思想。

    引用的有点长,是为了照顾一下新手。
    精简一下 无状态 重结果 输入决定输出

    到了这里自然会想到 面向过程。面向过程编程,相信计算机专业的同学都知道它是比面向对象还要早的编程思想。

    面向过程编程也就是结构化程序设计,最为接近自然语言的一种编码方式。在面向过程程序设计中,问题被看作一系列需要完成的任务,函数则用于完成这些任务,解决问题的焦点集中于函数。采用自顶向下、逐步求精的程序设计方法,使用三种基本控制结构构造程序,即任何程序都可由顺序、选择、循环三种基本控制结构构造。

    面向过程编程并没有对实际编程中是否可以记录程序状态进行明确规定。实际上多数时候也都是用到了状态记录,所以在我的看法里暂且理解为: 面向过程面向对象 的前身,面向对象面向过程 的封装。

    • 响应式编程
      先看一下维基百科上的定义

    In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm it is possible to express static (e.g., arrays) or dynamic (e.g., event emitters) data streams with ease, and also communicate that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the changed data flow.
    在计算机中,响应式编程或反应式编程(英语:Reactive programming)是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

    数据流的方式不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,事件像流一样的传播出去,产生一系列影响,借用面向对象的一句话,万物皆是流。

    一大堆定义和描述是不是很抽象?


    需求:IG拿冠军
    分解成过程:

    1. 勤学苦练
    2. 去韩国参加比赛
    3. 进四强
    4. 打赢其他国家

    面向过程:写下上面的四个函数,按顺序调用一下组成程序。

    函数式:详细拆分一下,比如勤学苦练() 去韩国() 参加比赛()等。谁都可以参加比赛,去韩国。调用的对象直接决定了返回结果,中间没有状态记录。王思聪去韩国跟IG去韩国同样都要办签证 ,函数外部不关心这个过程,只关心是否到韩国的结果。那么在得到是否到达韩国这个结果之后把这个结果作为下一个函数的输入继续调下一个函数,如此循环实现IG拿冠军的最终需求。

    响应式:IG勤学苦练开始,中间绑定去韩国比赛,进四强等诸多事件,最终夺冠。实现一勤学苦练就触发后续的诸多事件发生到最终的夺冠。强调的是一个事件的产生像流一样传播出去,像蝴蝶效应一样影响很多东西和结果。至于中间的这个绑定如何实现,并不关心。

    PS: 响应式会有副作用,比如IG参加比赛的副作用 副作用

    相关文章

      网友评论

          本文标题:ReactiveCocoa系列之一:基础

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