精华内容
下载资源
问答
  • 函数响应式编程

    2017-08-16 09:26:47
    一次内部分享的PPT,从函数式编程的优点到响应式结合原因,Rx的周期,应用的举例,源代码的解读,到MVVM的应用的介绍
  • 什么是函数响应式编程? 响应式编程思想为体,函数式编程思想为用。首先我们要来了解什么是函数式编程和响应式编程。 什么是函数式编程? 顾名思义,函数式编程就是用函数来解决问题的编程方式,几乎任何一门语言都...

    什么是函数响应式编程?

    响应式编程思想为体,函数式编程思想为用。首先我们要来了解什么是函数式编程和响应式编程。

    什么是函数式编程?

    顾名思义,函数式编程就是用函数来解决问题的编程方式,几乎任何一门语言都支持函数,但是函数式编程具有几个特点:

    1. 声明式(Declarative)
    2. 纯函数(Pure Functio
    3. 数据不可变性(Immutability)

    在深入了解这三个特点之前,我们需要知道JavaScript是不是函数式编程?
    有的语言生来就是函数式编程,比如Haskell、Lisp这些语言本身就强制要求代码遵从以上三个要求,不过JavaScript并没有要求数据不可变性,也就是说用JavaScript写的函数并不能保证没有任何副作用。

    那到底javascript是不是函数式编程?

    从语言的角度上讲,JavaScript当然不算一个纯粹意义上的函数式编程语言,但是JavaScript的函数具有第一公民的身份,因为函数本身就是一个对象,可以被赋值给一个对象也可以作为一个参数传递,可以很方便的应用函数式编程的思想。虽然javascript不算纯粹意义上的编程语言,但是通过一些编程规范和一点工具的帮助,完全可以写出函数式的代码。
    RxJs就是辅助我们写出函数式代码的一种工具。 接下来,我们分别介绍JavaScript如何满⾜函数式编程的特性需要。

    声明式

    和声明式相对应的编程⽅式叫做命令式编程(ImperativeProgramming),命令式编程也是最常见的⼀种编程⽅式。
    先来看⼀个命令式编程的例⼦,我们要实现⼀个功能,将⼀个数组中每个元素的数值乘以2。这样⼀个功能可以实现为⼀个叫double的函数,代码如下:

    function double(arr) {
    	const results = [];
    	for (let i = 0; i < arr.length; i++){
    		results.push(arr[i] * 2)
    	};
    	return results;
    };
    

    代码解析:

    1. 创建一个results的空数组
    2. 创建一个for循环,然后每次把arr数组的每一个值乘2,再利用push方法添加到results中
    3. 返回 results数组

    这个double函数的实现没有任何⽑病,但是,我们又来了⼀个需求:实现⼀个函数,能够把⼀个数组的每个元素加⼀。
    我们可以再实现⼀个addOne函数,代码如下:

    function addOne(arr) {
    	const results = [];
    	for (let i = 0; i < arr.length; i++){
    		results.push(arr[i] + 1)
    	};
    	return results;
    };
    

    如果比较一下double和addOne这两个函数,发现除了函数名和push的参数不一样之外其余的代码都是一样的,这就出现了重复代码的问题(重复代码可能是所有软件中邪恶的根源)。我们可以看到命令式编程的一个大问题,我们应该改进它。
    我们可以利用javascript中的map方法,来重新实现double和addOne函数,代码如下:

    function double(arr) {
    	return arr.map(function(item) {return item * 2});
    }
    function addOne(arr) {
    	return arr.map(function(item) {return item + 1});
    }
    

    代码简洁了很多,没有了for循环,也没有了push的操作,因为这些都被封装在map方法里了。map方法主要提供了一个函数类型的参数,这个参数可以定制对每一个数组元素如何处理。我们来看double中这个定制函数,代码如下:

    function(item) {return item * 2};
    

    这个函数实现了这样⼀个功能:不管传⼊什么数据,都会返回这个数据乘以2的结果。这就是声明式编程,因为在double函数中,代码实际上是这样⼀种解读:把⼀个数组映射(map)为另⼀个数组,每个对应的元素都乘以2。我们可以把上面的代码通过es6的箭头函数再做一次优化,代码如下:

    const double = arr => arr.map(item => item * 2);
    const addOne = arr => arr.map(item => item + 1);
    

    这就是声明式编程,比命令是编程更容易维护,代码更简洁,更具有表现力。

    纯函数

    还是以上面提到的double函数为例,这一次我们从使用者的角度来看,代码如下:

    const oneArray = [1, 2, 3];
    const anotherArray = double(oneArray);
    // anotherArray的内容为[ 2, 4, 6 ]
    // oneArray的内容依然是[ 1, 2, 3 ]
    

    我们先声明⼀个oneArray,然后将oneArray作为参数传递给double函数,返回的结果赋值给anotherArray,因为double的实现遵从了保持数据不可改的原则,所以oneArray数组依然是以前的值,⽽anotherArray是⼀个全新的数组,这两个数组中的数据相互独⽴,互不⼲扰。所以,我们说double就是⼀个“纯函数”。所谓纯函数,指的是满⾜下⾯两个条件的函数:

    • 函数的执行过程由参数决定,不会受除参数之外的任何数据影响。
    • 函数不会修改任何外部状态,比如修改全局变量传入的参数。

    表⾯上看起来,纯函数的要求,是限制了我们编写函数的⽅式,似乎让我们没法写出“更强⼤”的函数,实际上,这种限制带来的好处远远⼤于所谓“更强⼤”的好处,因为,纯函数让我们的代码更加简单,从⽽更加容易维护,更加不容易产⽣bug。
    我们还是通过⼀段代码例⼦来说明,代码如下:

    function arrayPush (arr, newValue) {
    	arr.push(newValue);
    	return arr;
    }
    const originalArray = [1, 2, 3];
    const pushedArray = arrayPush(originalArray, 4);
    const doubledPushedArray = double(pushedArray);
    // pushedArray值应该是[ 1, 2, 3, 4 ]
    // doubledPushedArray值应该是 [ 2, 4, 6, 8 ]
    

    当上⾯的语句执⾏完毕之后,originalArray的值却是[ 1, 2, 3, 4 ],为什么呢,因为push的操作会改变原数组,也就是说会改变传递过来的参数originalArray 数组。而函数式编程的一个条件就是不能修改任何外部状态,所以我们不能使用push操作,但是我们可以用es6的扩展运算符…达到纯函数的条件 ,代码如下:

    function arrayPush (arr, newValue) {
    	return [...arr, newValue];
    }
    

    和纯函数相反的就是“不纯函数”(Impure Function),⼀个函数之所以不纯,可能做了下⾯这些事情:

    • 改变全局变量的值。
    • 改变输⼊参数引⽤的对象,就像上⾯不是纯函数的arrayPush实现。
    • 读取⽤户输⼊,⽐如调⽤了alert或者confirm函数。
    • 抛出⼀个异常。
    • 操作浏览器的DOM。
    • ⽹络输⼊/输出操作,⽐如通过AJAX调⽤⼀个服务器的API。

    上⾯还只是不纯函数的⼀部分表现,其实,有⼀个很简单的判断函数纯不纯的⽅法,就是假设将⼀个函数调⽤替换为⼀个预期返回的常数,程序运⾏结果是否一样。

    数据不可变

    程序要好发挥作⽤当然是要产⽣变化的数据,但是并不意味着必须要去修改现有数据,替换⽅法是通过产⽣新的数据,来实现这种“变化”,也就是说,当我们需要数据状态发⽣改变时,保持原有数据不变,产⽣⼀个新的数据来体现这种变化。
    不可改变的数据就是Immutable数据,它⼀旦产⽣,我们就可以肯定它的值永远不会变,这⾮常有利于代码的理解。在JavaScript中,字符串类型、数字类型就是不可改变的数据,使⽤这两种类型的数据给你带来的⿇烦⽐较少。相反,JavaScript中⼤部分对象都是可变的,⽐如JavaScript⾃带的原⽣数组类型,数组的push、pop、sort函数都会改变⼀个数组的内容,由此引发的bug可不少。这些不纯的函数导致JavaScript天⽣不是⼀个纯粹意义上的函数式编程语⾔。

    什么是响应式编程?

    响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。如果你使⽤过Excel的公式功能,你就已经应⽤过响应式编程。再例如,在命令式编程环境中,a:=b+c表示将表达式的结果赋给a,而之后改变b或c的值不会影响a。但在响应式编程中,a的值会随着b或c的更新而更新。
      响应式编程最初是为了简化交互式用户界面的创建和实时系统动画的绘制而提出来的一种方法,但它本质上是一种通用的编程范式。例如,在MVC软件架构中,响应式编程允许将相关模型的变化自动反映到视图上,反之亦然。

    展开全文
  • 函数响应式编程 - Functional Reactive Programming 函数式编程 - Functional Programming 函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。我...

    常见编程模式


    函数式编程 - Functional Programming

    函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。我们可以通过组合不同的函数来得到想要的结果

    函数式编程是用递归做为控制流程的机制

    函数编程典型实例是高阶函数:
    Fun

    详情请参考【阮一峰的日志】函数式编程初探

    函数式编程特点:

    • 大量使用函数,减少了代码的重复,代码简洁,开发速度较快
    • 接近自然语言,易于理解
    • 易于”并发编程”

    响应式编程 - Reactive Programming

    维基百科 - 响应式编程

    响应式编程是一种面向数据流和变化传播的编程范式

    学习响应式编程最重要的是理解响应式思维

    响应式编程就是用异步数据流进行编程

    • 流就是一个按时间顺序正在进行的事件序列(A stream is a sequence of ongoing events ordered in time)
    • 可以基于任何东西创建数据流。流非常轻便,并且无处不在,任何东西都可以是一个流:变量,用户输入,属性,缓存,数据结构等
    • 可以对这些流进行合并、过滤、转变等

    例如,在命令式编程环境中

    a = b + c

    表示将表达式 b + c 的结果赋给 变量 a, 而之后改变 bc 的值不会影响a

    但在响应式编程中,a 的值会随着 bc 的值改变而改变


    流可以发送 3 种不同的事物:

    • 一个值(Any Type)
    • 一个错误 (Error)
    • 或一个已完成 (Completed) 信号

    PS: 在一个流中, Error 和 Completed 只可以发送其中一个

    对流的 “监听” 又称为 订阅(Subscribing),而定义的函数即为 观察者(Observer),流就是 主题(Subject, Observable)。这是一个典型的观察者模式。

    响应式编程特点:

    • 提高了代码的抽象水平,只需专注那些定义业务逻辑的事件的依存关系,而无需写大量的实现细节
    • 代码会更加简洁
    • 响应式思维, 比较抽象

    函数响应式编程 - Functional Reactive Programming

    函数响应式编程是 函数式编程响应式编程 的结合

    函数响应式编程 = 函数式编程 + 响应式编程

    函数响应式编程结合了 FP 和 RP 的特点:

    • 通过不同的构建函数,来创建所需要的数据序列,最后通过适当的方式来响应这个序列
    • 简单点说可以把若干不同数据在不做任何修改的情况下转换成新的数据,一直交替直到符合系统需求的数据

    FRP的几种常见事件流处理方法:

    • 映射 (map、 flatMap…)
    • 过滤 (filter…)
    • 合并 (merge、 combine…)
    • 条件 (skip…)

    简单代码示例:

    getDataFromNetwork()
      .skip(10)
      .take(5)
      .map({ s -> return s + " transformed" })
      .subscribe({ println "onNext => " + it })
    展开全文
  • Rxswift学习之(一)函数响应式编程思想1. 函数响应式编程思想简介 1. 函数响应式编程思想简介 维基百科对函数式编程的解释 在计算机科学里,函数式编程是一种编程范式,它将计算描述为表达式求值并避免了...

    1. 函数响应式编程思想必备基本概念简介

    • 函数式编程

    在计算机科学里,函数式编程是一种编程范式,它将计算描述为表达式求值并避免了状态和数据改变。函数式编程里面的“函数”指的是数学函数。
    函数式编程思想:是将操作尽可能写在一起!嵌套的函数!!
    本质:就是往方法里面传入Block,方法中嵌套Block调用.
    了解函数式编程首先要理解命令式编程,命令式编程往往是我们大多数人固有的编程思维,这中模式依赖于我们希望自己的程序如何完成某项任务,而与之对应的是声明式编程(Declarative Programming),它关心是任务的目的是什么,而不是具体如何实现。这把程序员从那些细枝末节中解放了出来。而函数响应式编程正是声明式编程的一个子范式。

    • 编程函数和数学函数对比
      在这里插入图片描述
      数学函数的特点是对于每一个自变量,存在唯一的因变量与之对应。而编程函数的特点是参数和返回值都不是必须的,函数可能依赖外界或者影响外界。

    • 命令式和函数式的区别

    所有的命令式语言都被设计来高效地使用冯诺依曼体系结构的计算机。实际上,最初的命令式语言的目的就是取代汇编语言,对机器指令进行进一步抽象。因此,命令式语言带有强烈的硬件结构特征。命令式语言的核心特性有:模拟存储单元的变量、基于传输操作的赋值语句,以及迭代形式的循环运算。命令式语言的基础是语句(特别是赋值),它们通过修改存储器的值而产生副作用(side effect)的方式去影响后续的计算。
    函数式语言设计的基础是Lambda表达式,函数式程序设计把程序的输出定义为其输入的一个数学函数,在这里没有内部状态,也没有副作用。函数式语言进行计算的主要是将函数作用与给定参数之上。函数式语言没有命令式语言所必需的那种变量,可以没有赋值语句,也可以没有循环。一个程序就是函数定义和函数应用的说明;一个程序的执行就是对函数应用的求值。

    详细教程参考:理解函数式编程

    • block
      block可以作为对象的属性,也可以作为方法的参数,也可以作为返回值。而作为返回值是链式编程的核心。
      常用的block用法如下:
    1. block表达式语法:
    ^返回值类型(参数列表){表达式}
    
    实例 1^int (int count) {
      return count + 1;
    };
    2. 声明类型变量的语法
    //返回值类型(^变量名)(参数列表) = block表达式
    
    实例 2:
    int (^sum)(int count) = (^int count) {
      return count + 1;
    };
    3. 作为函数参数的语法
    - (void)func(int (^)(int))block {
    
    }
    
    //定义block简写
    typedef int (^Sumblock)(int);
    
    - (void)func(Sumblock)block {
    
    }
    
    • 高阶函数

    高阶函数实际上就是函数的函数,它是所有函数式语言的性质。函数式语言中,函数作为第一等公民,这也意味着你像定义或调用变量一样去定义或调用函数。可以在任何地方定义,在函数内或函数外,可以将函数作为参数或者返回值。在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

    1 接受一个或多个函数作为输入
    2 输出一个函数
    在数学中它们也叫做算子(运算符)或泛函。微积分中的导数就是常见的例子,因为它映射一个函数到另一个函数。

    关于Swift的相关高阶函数可以参考我的一篇博客:Swift的高阶函数

    2. iOS中三种编程思想:链式、函数式和响应式编程

    2.1 链式编程

    1. 特点:方法的返回值必须是方法的调用者
    2. 链式写法对比普通写法
    • 普通写法:

    实例 3

    @interface Person : NSObject
    
    - (void)eat;
    - (void)sleep;
    
    @end
    
    @implementation Person
    
    - (void)eat
    {
        NSLog(@"%s", __FUNCTION__);
    }
    
    - (void)sleep
    {
        NSLog(@"%s", __FUNCTION__);
    }
    
    @end
    
    ViewController.m
    Person *person = [[Person alloc] init];
    //调用时必须单个调用,而且不能任意组合顺序
      /** 普通的调用方式  */
        [person eat];
        [person sleep];
    
    • 链式写法:
      将上面的普通写法改为链式,方法增加一个返回值,且返回值为调用者本身。代码如下:

    实例 4

    // 链式写法
    Person.h
    - (Person *)eat;
    - (Person *)sleep;
    
    Person.m
    - (Person *)eat
    {
        NSLog(@"%s", __FUNCTION__);
        return self;
    }
    
    - (Person *)sleep
    {
        NSLog(@"%s", __FUNCTION__);
        return self;
    }
    
    ViewController.m
        Person *person = [[Person alloc] init];
      /** 链式写法,这样不仅可以无限调用,而且可以控制顺序 */
        [[person eat] sleep];
        [[person sleep] eat];
        [[person eat] eat];
    
        /** 通过”点”语法,将需要执行的代码块连续的书写下去,就是链式编程.它能使代码简单易读,书写方便 */
        person.eat.sleep.eat.sleep.sleep;
    
    
    1. 此外我们还可以将blockly作为返回值—链式编程带参数
      代码如下:

    实例 5

    Person.h
    - (Person *(^)(NSString *food))eat3;
    - (Person *(^)(NSString *where))sleep3;
    
    Person.m
    - (Person *(^)(NSString *food))eat3
    {
        return ^(NSString *food) {
            
            NSLog(@"吃:%@ ",food);
            
            return self;
        };
    }
    
    - (Person *(^)(NSString *where))sleep3
    {
        return ^(NSString *where) {
            
            NSLog(@"睡在:%@上",where);
            
            return self;
        };
    }
    
    ViewController.m
        Person *person = [[Person alloc] init];
    
        /** 链式 + 函数式写法 */
        person.sleep3(@"床").eat3(@"苹果").eat3(@"香蕉").sleep3(@"沙发");
    
        返回值block不带参数,()不传参即可
        person.sleep3().eat3().eat3().sleep3();
    

    2.2 函数式编程

    函数式编程思想:是将操作尽可能写在一起!嵌套的函数!!
    本质:就是往方法里面传入Block,方法中嵌套Block调用.

    如下代码:

    实例 6

    /** 返回调用者本身,获取其它属性和方法 */
    - (Person *)calculator:(NSInteger(^)(NSInteger result))block
    {
        _result = block(_result);
        
        return self;
    }
    
       Person *person = [[Person alloc] init];
    /** 计算器 */
       Person *calculatPerson = [person calculator:^NSInteger(NSInteger result) {
           
            result = result + 10;
            result = result*10;
            return result;
        }];
        
        NSLog(@"%ld", calculatPerson.result);
    

    简单理解就是将block块作为函数的参数,可以异步在参数block回调,将函数运行所需要的一些信息或者产出的一些结果通过block传递。代码逻辑清晰。

    2.3 响应式编程

    1. 响应式编程解决的痛点:
      在程序开发中:a = b + c赋值之后 b 或者 c 的值变化后,a 的值不会跟着变化;如果我们有这么一个需求:如果 b 或者 c 的数值发生变化,a 的数值会同时发生变化。怎么实现呢?
      响应式编程就是为这个而生的。如我们经典的KVO(详情可以参考我的一篇博客 KVO 底层实现原理),经典的响应式编程框架RAC。当然少不了我们的主角Rxswift.

    列如:

    实例 7

    a = 2
    b = 2
    c = a + b // c is 4
    b = 3
    // now what is the value of c?
    

    在响应式编程中,c的值将会随着b的值改变而改变。
    FRP提供了一种信号机制来实现这样的效果,通过信号来记录值的变化。信号可以被叠加、分割或合并。通过对信号的组合,就不需要去监听某个值或事件.
    如下图:
    在这里插入图片描述

    可以把信号想象成水龙头,只不过里面不是水,而是玻璃球(value),直径跟水管的内径一样,这样就能保证玻璃球是依次排列,不会出现并排的情况(数据都是线性处理的,不会出现并发情况)。水龙头的开关默认是关的,除非有了接收方(subscriber),才会打开。这样只要有新的玻璃球进来,就会自动传送给接收方。可以在水龙头上加一个过滤嘴(filter),不符合的不让通过,也可以加一个改动装置,把球改变成符合自己的需求(map)。也可以把多个水龙头合并成一个新的水龙头(combineLatest:reduce:),这样只要其中的一个水龙头有玻璃球出来,这个新合并的水龙头就会得到这个球。

    3. 什么是函数响应式编程

    什么是函数响应式编程呢? 可以一句话概括:

    • 响应式思想为体,函数式编程思想为用
      在这里插入图片描述
    1. 函数式

    特点:代码简洁(复用)、易于理解(接近自然语言)、便于代码管理
    例如:doSomething1().doSomething2().doSomething3()一系列的动作简洁明了,相互不干扰,可以合并使用也可以分开单独使用.

    实例 8:下面代码清楚的讲解了函数式编程与普通处理方式的区别。

    //例1:
    //遍历数组(要求:1.首先获取 > 3的数字;2.获取的数字之后 + 1;3.所有数字中的偶数;4.可读性 清晰度)
     
    let array = [1,2,3,4,5,6,7]
     
    //普通处理方式:
            for num in array{
                if num > 3{
                    let number = num + 1
                    if (number % 2 == 0) {
                        print(number)
                    }
                }
            }
    //函数式:
    array.filter{ $0 > 3}
         .filter{ ($0+1) % 2 == 0 }
         .forEach { print($0) }
    
    

    从代码中我们可以明显的对比出代码的优劣,普通代码实现for循环需要层层嵌套,非常累赘,可读性不高;而利用高阶函数实现的函数式编码代码清晰简洁。你可以注释掉中间的一个.filter{ ($0+1) % 2 == 0 }代码一样可以正常运行,非常灵活。

    1. 响应式

    对象对某一数据流变化做出响应的这种编码方式称为响应式。如对象A和对象B,A和B有一种“说不清”的关系,A要时刻监控B的行为,对B的变化也做出相应的变化。那么怎么实现呢?对于B来说,B做任何事都需要向A汇报,这样A就能实时监控B的行为,并响应。在iOS开发中我们经常会响应一些事件,button、tap、textField、textView、notifaction、KVO、NSTimer等等这些,都需要做响应监听,响应后都需要在对应的响应事件中去做处理,而原生开发中,触发对象与响应方法是分离的,如button的初始化和点击响应方法是分离的。

    在程序开发中:a = b + c赋值之后 b 或者 c 的值变化后,a 的值不会跟着变化;
    响应式编程目标就是:如果 b 或者 c 的数值发生变化,a 的数值会同时发生变化;
    响应编程的经典案例:KVO
    响应式编程框架:ReactiveCocoa(RAC) 详细学习可以参考:ReactiveCocoa博客

    响应式编程的优点:

    1) 开发过程中,状态以及状态之间依赖过多,RAC更加有效率地处理事件流,而无需显式去管理状态。在OO或者过程式编程中,状态变化是最难跟踪,最头痛的事。这个也是最重要的一点。
    2) 减少变量的使用,由于它跟踪状态和值的变化,因此不需要再申明变量不断地观察状态和更新值。
    3) 提供统一的消息传递机制,将oc中的通知,action,KVO以及其它所有UIControl事件的变化都进行监控,当变化发生时,就会传递事件和值。
    4) 当值随着事件变换时,可以使用map,filter,reduce等函数便利地对值进行变换操作。

    下面统一对比Rxswift的UIButton事件实现和原生的实现方式,了解一下响应式编程的优点。

    实例 9 :对比传统UIButton响应事件写法和Rxswift中函数响应式写法的区别。

    1. 传统写法
    //传统写法,UI代码和逻辑是分开的,为了监听一个按钮的响应事件,我们需要在另外一处地方实现。这样可读性不好,代码繁琐。
    button = UIButton()
    button.addTarget(self, action: #selector(text), for: .touchUpInside)
    @objc func text() {
       print("Button clicked!")
    }
    
    2. Rxswift写法
    //Rxswift的实现就简单多了,而且目标非常明确,就是三部曲:1创建序列,2,订阅响应消息,3.析构销毁
    //当你订阅了响应消息后,只要序列发生了变化,订阅的消息总会触发,如下面的代码,当你订阅了按钮的点击事件后,每次点击按钮,订阅的消息subscibe就会收到一次。
    self.button.rx.tap    //序列,这里默认的序列是默认是.onTouchUpInside事件
                .subscribe(onNext: { () in   //订阅
                    print("Button clicked!")
                }, onError: { (error) in //当Rxswift的事件链走不通,会回调这个onError,通知错误
                    print("错误信息")
                }, onCompleted: {//当Rxswift订阅的所有事件链走完了,会回调这个onCompleted,告知执行完毕,这个和onError是对立互斥的,两者只会发生一个。
                    print("订阅完成")
                })
                .disposed(by: DisposeBag())    //销毁
    
    

    这里 taps 就是按钮点击事件的序列。然后我们通过打印“Button clicked!”,来对每一次点击事件做出响应。这种编程方式叫做响应式编程。我们结合函数式编程以及响应式编程就得到了函数响应式编程。通过不同的构建函数,来创建所需要的数据序列。最后通过适当的方式来响应这个序列。这就是函数响应式编程。

    通过上面的代码对比分析,我们可以看出Rxswift写出来的代码是多么简介,可读性逻辑清晰,看每一行就知道在做什么,不需要想原生UI响应和逻辑分开的,看代码需要跳来跳去的。通过一个这么小小的实例代码,我们初次见识到了Rxswift的强大,然而这个只是Rxswift框架功能的冰山一角。我强烈推荐大家都来学习下这么优秀的开源框架。

    下面我们来窥探一下Rxswift到底是个什么东东,为啥这么牛逼。

    4. Rxswift简介

    4.1 什么是 ReactiveX(Reactive Extensions)

    An API for asynchronous programming with observable streams
    通过可观察的流实现异步编程的一种API(不明白?嗯,看完所有的例子再读一篇)
    ReactiveX is more than an API, it’s an idea and a breakthrough in programming. It has inspired several other APIs, frameworks, and even programming languages.
    ReactiveX 不仅仅是一种 API 那么简单,它更是一种编程思想的突破。它已经影响了其他 API,frameworks,以及编程语言。

    总的一句话概括:
    ReactiveX(Reactive Extensions)是通过可观察的流实现异步编程的一种API,它结合了观察者模式、迭代器模式和函数式编程的精华。RxSwift 是 ReactiveX 编程思想的一种实现,几乎每一种语言都会有那么一个 Rx[xxxx] 框架,比如Rxswift, RxJava,RxJS 等等。

    4.2 Rx的基本概念

    Rx是一个家族,他们把函数响应式编程思想使用到了极致,要学习Rxswift必须先了解一下一些基本概念。

    • 观察者模式 Observable:
      对某些数据流(很广,可以是一些事件等)进行处理,使其变成可观察对象(Observable)序列,这样观察者(observer)就可以订阅这些序列;

    Observable 直译为可观察的,它在 RxSwift 起到了举足轻重的作用,在整个 RxSwift 的使用过程中你会经常与它打交道。如果你使用过 RAC ,它如同 Signal 一样。RxSwift 中关键点就是在于如何把普通的数据或者事件变成可观察的,这样当某些数据或事件有变化的时候就会通知它的订阅者。RxSwift 中提供很多种创建 Observable 创建方法。比如:From、never、empty 和 create 等,更多创建方法。订阅者可以收到 3 个事件,onNext、onError 和 onCompleted,每个 Observable 都应该至少有一个 onError 或 onCompleted 事件,onNext 表示它传给下一个接收者时的数据流。

    在Rxswift中我们把它理解为:观察者序列,(就是一系列可以被监听,观察的事件等,当你订阅了这些序列,你就可以在闭包中监听到对应的事件)通过下面一个图可以更好的理解Observable:

    在这里插入图片描述
    序列监听有三个步骤:1.创建序列,2订阅序列,3.销毁序列。当创建序列,并订阅了序列后,只要某个事件发送了序列消息,就可以在订阅的闭包里面监听到。下面我们一个小的实例来加深理解:

    实例 10

    
    //1:创建序列
    //利用函数式编程思想,在create()构造函数中传入一个闭包,这个闭包会被类对象保存起来,后续每个时间,事件触发的时候会回调这个传入的闭包,这样就像连接了一个链条一样,顺着链条就可找到需要调用的闭包。
     let ob = Observable<Any>.create { (observer) -> Disposable in
                // 3:发送信号
                obserber.onNext([1,2,3,4])
                obserber.onCompleted()
    //            obserber.onError(NSError.init(domain: "error!", code: 10087, userInfo: nil))
                return Disposables.create()
     
    //2:订阅信息
    //当我们订阅了Observable的消息后,只要Observable的事件触发,都会通过OnNext这个闭包告诉我们。
     let _ = ob.subscribe(onNext: { (text) in
                print("订阅到:\(text)")    //这里会监听到订阅的Observable消息
            }, onError: { (error) in
                print("error: \(error)")    //当发生错误时,会回调这里
            }, onCompleted: { // 当所有序列执行完毕时,会回调这里。
                print("完成")
            }) {
                print("销毁")
            }
    
    
    

    如果你仔细观察这里的代码,会有一个疑问:从订阅中心observer,一直在用的序列,序列内部的代码是不曾看到的。为什么从序列闭包里面的发出信号,订阅信号的闭包里面能够订阅到? 这个问题我们将会在Rxswift 序列核心逻辑浅析 中详细分析

    • 操作符 Operators:
      然而对于订阅者来说(observer)某些选项(items)并不是自己需要的(需要过滤),某些选项(items)需要转换才能达到自己的目的;

    Observable 创建后,可能为了满足某些需求需要修改它,这时就需要用到操作符。RxSwift 提供了非常多的操作符,当然不必要一一掌握这些操作符,使用的时候查一下即可,当然常见的操作符必须要掌握,比如 map、flatMap 、create 、filter 等。Operators详细介绍

    实例 11
    这个例子主要把查找数组中的字符串 kongyulu,并显示到 Label 上。

    override func viewDidLoad() {
        super.viewDidLoad()
        DispatchQueue.global().async {
            self.from()
        }
    }
        
    func from() {
        Observable.from(["haha", "kongyulu", "cc", "wswy", "Rx"])
            .subscribeOn(MainScheduler.instance)
            .filter({ (text) -> Bool in
                return text == "kongyulu"
            })
            .map({ (text) -> String in
                return "my name is: " + text
            })
            .subscribe(onNext: { [weak self] (text) in
                self?.nickNameLabel.text = text
            })
            .disposed(by: disposeBag)
    }
    
    • 迭代模式 Iterator:
      这样集合或者序列中的值就可以进行遍历了。

    • 调度器 Scheduler:
      为了提升用户体验,或其它目的,有些操作需要放到特定的线程去执行,比如 UI 操作需要放到主线程,这就涉及到了调度器。

    如果你想给 Observable 操作符链添加多线程功能,你可以指定操作符(或者特定的Observable)在特定的调度器(Scheduler)上执行。对于 ReactiveX 中可观察对象操作符来说,它有时会携带一个调度器作为参数,这样可以指定可观察对象在哪一个线程中执行。而默认的情况下,某些可观察对象是在订阅者订阅时的那个线程中执行。SubscribeOn 可以改变可观察对象该在那个调度器中执行。ObserveOn 用来改变给订阅者发送通知时所在的调度器。这样就可以使可观察对象想在那个调度器中执行就在那个调度器中执行,不受约束,而这些细节是不被调用者所关心的。犹如 GCD 一样,你只管使用,底层线程是咋么创建的,你不必关心。

    4.3 Rxswift框架的优点

    4.4 Rxswift框架安装

    Rxswift就是一个框架而已,通过pod安装跟其他框架没有什么差异
    在podfile中写入

      pod 'RxSwift'
      pod 'RxCocoa'
    

    命令行执行pod install就可以了。

    4.5 Rxswift简单使用

    5. Rxswift 序列核心逻辑浅析

    实例 10中我们留下一个疑问:从订阅中心observer,一直在用的序列,序列内部的代码是不曾看到的。为什么从序列闭包里面的发出信号,订阅信号的闭包里面能够订阅到?

    下面我们将一步步通过Rxswift的源码分析来揭开这个疑团。

    在分析代码前我们先回顾一下序列的三部曲:1.创建序列,2,订阅序列,3,销毁序列,其中在2中订阅序列之后,为什么我们就能监听到序列呢,理解了这个逻辑后,我们的疑问就会自然而解。

    先看一个简单的类图:
    在这里插入图片描述
    接着我们来研究一下这段代码的执行逻辑

     1:创建序列
            // AnonymousObservable -> producer.subscriber -> run
            // 保存闭包  - 函数式 保存 _subscribeHandler
            //
            let ob = Observable<Any>.create { (obserber) -> Disposable in
                // 3:发送信号
                obserber.onNext("框架班级")
                obserber.onCompleted()
    //            obserber.onError(NSError.init(domain: "coocieeror", code: 10087, userInfo: nil))
                return Disposables.create()
            }
            
            
      2:订阅信号
            // AnonymousObserver  - event .next -> onNext()
            // _eventHandler
            // AnonymousObservable._subscribeHandler(observer)
            // 销毁
            let _ = ob.subscribe(onNext: { (text) in
                print("订阅到:\(text)")
            }, onError: { (error) in
                print("error: \(error)")
            }, onCompleted: {
                print("完成")
            }) {
                print("销毁")
            }
    

    用一个图来表达上面这段代码执行时,Rxswift框架做的事情如下:
    在这里插入图片描述
    现在我们来研究一下源码的具体实现:

    1. 创建序列
      当我们调用let ob = Observable<Any>.create { (obserber) -> Disposable in } 这行代码时,进入Rxswift源码可以看到实际调用了:
    extension ObservableType {
    
        /**
             Creates an observable sequence from a specified subscribe method implementation.
        
             - seealso: [create operator on reactivex.io](http://reactivex.io/documentation/operators/create.html)
        
             - parameter subscribe: Implementation of the resulting observable sequence's `subscribe` method.
             - returns: The observable sequence with the specified implementation for the `subscribe` method.
             */
        public static func create(_ subscribe: @escaping (RxSwift.AnyObserver<Self.E>) -> Disposable) -> RxSwift.Observable<Self.E>
    }
    
    1. 保存序列到对象中
      根据这个函数的官方注释,我们得知create()这个方法是在Create.swift中实现的
      在这里插入图片描述
    2. 接着我们订阅消息
      当我执行let _ = ob.subscribe(onNext: { (text) }, onError: { (error) in }, onCompleted: { }) 这行代码时,我们就订阅了消息,我们分析源码得知,Rxswift实际上是调用了
      在这里插入图片描述
      我们进入源码查看该方法的实现:
     /**
         Subscribes an element handler, an error handler, a completion handler and disposed handler to an observable sequence.
         
         - parameter onNext: Action to invoke for each element in the observable sequence.
         - parameter onError: Action to invoke upon errored termination of the observable sequence.
         - parameter onCompleted: Action to invoke upon graceful termination of the observable sequence.
         - parameter onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has
         gracefully completed, errored, or if the generation is canceled by disposing subscription).
         - returns: Subscription object used to unsubscribe from the observable sequence.
         */
        public func subscribe(onNext: ((E) -> Void)? = nil, onError: ((Swift.Error) -> Void)? = nil, onCompleted: (() -> Void)? = nil, onDisposed: (() -> Void)? = nil)
            -> Disposable {
                let disposable: Disposable
                
                if let disposed = onDisposed {
                    disposable = Disposables.create(with: disposed)
                }
                else {
                    disposable = Disposables.create()
                }
                
                #if DEBUG
                    let synchronizationTracker = SynchronizationTracker()
                #endif
                
                let callStack = Hooks.recordCallStackOnError ? Hooks.customCaptureSubscriptionCallstack() : []
                
                let observer = AnonymousObserver<E> { event in
                    
                    #if DEBUG
                        synchronizationTracker.register(synchronizationErrorMessage: .default)
                        defer { synchronizationTracker.unregister() }
                    #endif
                    
                    switch event {
                    case .next(let value):
                        onNext?(value)
                    case .error(let error):
                        if let onError = onError {
                            onError(error)
                        }
                        else {
                            Hooks.defaultErrorHandler(callStack, error)
                        }
                        disposable.dispose()
                    case .completed:
                        onCompleted?()
                        disposable.dispose()
                    }
                }
                return Disposables.create(
                    self.asObservable().subscribe(observer),
                    disposable
                )
        }
    

    分析源码,可以看到,我们在订阅消息注册的回调:onNext, onError,onComplete, onDisposed这个四个闭包都是作为函数的参数,在调用ob.subscribe()这个方法是作为参数传入进来的,在上面代码中定义了一个逃逸闭包 let observer = AnonymousObserver {} 在这个闭包内部,当调用这个逃逸闭包的调用者传递不同的event就会调用我们传入的相应闭包,这样我们就监听到了订阅的消息。如下图:
    在这里插入图片描述

    这里我们可以得知,只有我们的观察者有了事件变化,只需要通知上面代码定义的observer这个参数闭包就可以了。

    现在我们前面提到的疑问的答案来了:
    在源码中我们只看到了return Disposables.create( self.asObservable().subscribe(observer), disposable 就结束了。 然后玄机就在这句代码,self.asObservable()其实就是我们的创建的序列,而subscribe()就回调了我们传入的observer闭包,而在这个observer就会调用我们船人都监听序列消息闭包onNext(), onError(),onCompleted().

    接下来我们可以分析下 subscribe(observer)是如何调用observer的

    1. subscribe(observer)是如何调用observer的?
      看看下面这张图就明白了:

    在这里插入图片描述

    5.1 Rxswift 序列核心逻辑流程总结

    通过上面的分析,到这里我们总结一下大致流程:

    1.调用create()创建序列时,首先创建了一个AnonymousObserver对象,在初始化时传递了一个闭包作为参数并且保存下来self._eventHandler = eventHandler。
    AnonymousObserver是匿名观察者,用于存储和处理事件的闭包。
    2. 然后在最后有self.asObservable().subscribe(observer)这样一行代码,asObservable返回的是对象本身。
    3. 然后调用subscribe这个函数并且把创建的AnonymousObserver对象传递过去,会来到AnonymousObservable这个类里面,但是发现这个类里面没有subscribe方法,我们往父类Producer里面找到这个方法。
    4. 在Producer源码里面我们发现调用了run方法,也就是AnonymousObservable这个类里面的run方法,把observer作为参数传过来。

     return CurrentThreadScheduler.instance.schedule(()) { _ in
                    let disposer = SinkDisposer()
                    let sinkAndSubscription = self.run(observer, cancel: disposer)
                    disposer.setSinkAndSubscription(sink: sinkAndSubscription.sink, subscription: sinkAndSubscription.subscription)
    
                    return disposer
                }
    

    在run方法中,先创建一个AnonymousObservableSink对象并持有observer,然后调用这个对象的run方法把self传递过去,也就是把observable作为参数。
    AnonymousObservableSink这个类将可观察者Observable和观察者Observer链接起来,实现事件的传递,起到一个桥梁的作用。

    1. 最后Producer父类会调用子类的run()方法,子类parent就是我们创建的序列。
    //parent就是我们创建的序列。
    func run(_ parent: Parent) -> Disposable {
            return parent._subscribeHandler(AnyObserver(self))
        }
    

    在这里触发了_subscribeHandler的调用,这里的_subscribeHandler就是之前create函数参数传入的闭包。也就是这段闭包代码:let ob = Observable<Any>.create { (obserber) -> Disposable in // 3:发送信号 obserber.onNext("框架班级") obserber.onCompleted() // obserber.onError(NSError.init(domain: "coocieeror", code: 10087, userInfo: nil)) return Disposables.create() }
    注意:parent._subscribeHandler(AnyObserver(self)) 这行代码把self转换成AnyObserver对象,也就是把AnonymousObservableSink对象转换成AnyObserver对象。

    1. 接着分析下AnyObserver源码,可以看到在构造函数中有一行代码self.observer = observer.on,就是把AnonymousObservableSink类的on函数赋值给AnyObserver类的observer变量。从这里就可以明白为什么这行代码observer.onNext(“发送信号”) 最终会触发AnonymousObservableSink.on事件。
    public struct AnyObserver<Element> : ObserverType {
        ...
        /// Construct an instance whose `on(event)` calls `observer.on(event)`
        ///
        /// - parameter observer: Observer that receives sequence events.
        public init<O : ObserverType>(_ observer: O) where O.E == Element {
            self.observer = observer.on
        }
        
        /// Send `event` to this observer.
        ///
        /// - parameter event: Event instance.
        public func on(_ event: Event<Element>) {
            return self.observer(event)
        }
    
        /// Erases type of observer and returns canonical observer.
        ///
        /// - returns: type erased observer.
        public func asObserver() -> AnyObserver<E> {
            return self
        }
    }
    

    通过上面这段源码我们也看见public func asObserver() -> AnyObserver<E> { return self }这个函数返回self 也就明白了之前说的 asObserver()为什么就是我们创建的序列。也就是这个非常牛逼的代码return Disposables.create( self.asObservable().subscribe(observer), disposable )
    这里的self.asObservable() 实际就是我们创建的序列。

    1. 分析AnonymousObservableSink.on事件源码,可以得知在收到.error, .completed事件时,会调用forwardOn()方法,而在fowardOn()方法里面会调用self._observer.on(event)。
      这里的_observer就是第二步调用subscribe函数里面创建的observer对象。
      会先进入到父类的ObserverBase的on方法。

    具体源码如下:

    1. AnonymousObservableSink类on方法
        func on(_ event: Event<E>) {
            #if DEBUG
                self._synchronizationTracker.register(synchronizationErrorMessage: .default)
                defer { self._synchronizationTracker.unregister() }
            #endif
            switch event {
            case .next:
                if load(self._isStopped) == 1 {
                    return
                }
                self.forwardOn(event)
            case .error, .completed:
                if fetchOr(self._isStopped, 1) == 0 {
                    self.forwardOn(event)
                    self.dispose()
                }
            }
    
    2.  Sink类的forwardOn方法
    final func forwardOn(_ event: Event<O.E>) {
            #if DEBUG
                self._synchronizationTracker.register(synchronizationErrorMessage: .default)
                defer { self._synchronizationTracker.unregister() }
            #endif
            if isFlagSet(self._disposed, 1) {
                return
            }
            self._observer.on(event)
        }
    3. ObserverBase的on方法
    func on(_ event: Event<E>) {
            switch event {
            case .next:
                if load(self._isStopped) == 0 {
                    self.onCore(event)
                }
            case .error, .completed:
                if fetchOr(self._isStopped, 1) == 0 {
                    self.onCore(event)
                }
            }
        }
        
    4. onCore()方法最终会调用之前保存的闭包_eventHandler(event)
     override func onCore(_ event: Event<Element>) {
            return self._eventHandler(event)
        }
    
    1. 通过一张图分析上面5的源码逻辑

    在这里插入图片描述

    1. 到此我们可以弄明白的最初的create()函数里面的observer是怎么调用到ob.subscribe()的。

    最后有两张图总结序列事件传递的核心流程

    在这里插入图片描述
    在这里插入图片描述

    参考文档:http://t.swift.gg/d/2-rxswift?nojs=1

    展开全文
  • 最近,RAC的名气可谓是越来越大,出于对技术的探索心(为了装逼),最近研究学习了一下RAC,以下是本人在学习中对其的一些粗浅的认识; 首先,什么是RAC,ReactiveCocoa时Github上的一个Cocoa ...RAC运用的是函数响应

    最近,RAC的名气可谓是越来越大,出于对技术的探索心(为了装逼),最近研究学习了一下RAC,以下是本人在学习中对其的一些粗浅的认识;

    首先,什么是RAC,ReactiveCocoa时Github上的一个Cocoa FRP框架,目的为了接管苹果的所有事件机制(addTarget,代理,通知,KVO,时钟,网络处理);从其强大的作用就可以看出,这是一个超重量级大框架,慎用!

    RAC运用的是函数响应式编程思想,那么什么是函数响应式编程思想,下面我们就从头说起:

    说到编程思想,我们熟悉的就是面向过程编程思想和面向对象编程思想,面向过程 即根据问题,一步一实现,极其不利于代码的阅读和维护;面向对象 是对代码的抽象处理,即万物皆对象,由不同的对象,调用起自身的方法,实现相应的功能

    下面来创建一个dog对象来具体分析下:


    dog的.h文件

    #import <Foundation/Foundation.h>


    @interface Dog : NSObject


    - (void)run;

    - (void)eat;


    dog的.m文件

    #import "Dog.h"


    @implementation Dog


    - (void)run {

        NSLog(@"%s"__FUNCTION__);

    }


    - (void)eat {

        NSLog(@"%s"__FUNCTION__);

    }

    以下是在控制器中,创建dog类;

    #import "ViewController.h"

    #import "Dog.h"


    @interface ViewController ()


    @end


    @implementation ViewController


    - (void)viewDidLoad {

        [super viewDidLoad];

        

        Dog *dog = [Person new];

        

        [dog run];

        [dog eat];



    以上就是面向对象编程的调用方法,只能单一点用,写起来比较繁琐;


    接下来介绍链式编程思想,所谓链式编程,就是多个方法的调用可以像链条一样连接在一起,使代码的书写更简洁,可读性更好;

    就像这样:

    [[dog run1] eat1];

    要想实现方法的这种调用方式,可以这样定义

    - (Dog *)run1 {

        NSLog(@"%s", __FUNCTION__);

        

        return self;

    }


    - (Dog *)eat1 {

        NSLog(@"%s", __FUNCTION__);

        

        return self;

    }

    即每个方法都返回调用它的对象,以供调用后面的方法,这样,返回的对象就像一个链条,连接起了每个方法;


    下面说一下函数式编程:

        dog.run2().eat2();

        dog.eat2().run2();

        dog.run2().run2().eat2().run2();

    要想实现这种调用方式,可以这样定义方法

    //返回一个BlockBlock返回self

    - (Dog * (^)())run2 {

        

        Dog * (^runBlock)() = ^ {


            NSLog(@"run2");


            return self;

        };  

        return runBlock;

    }


    - (Dog *(^)())eat2 {

        

        return ^ {


            NSLog(@"");


            return self;

        };

    }


    通过Block返回的对象,调用后面的方法,从而实现了这种调用方式;当然如果想有参数,还可以这么写

    - (Dog *(^)(NSString *))eat3 {

        

        return ^ (NSString *food) {

            NSLog(@"%@", food);

            

            return self;

        };

    }


    - (Dog *(^)(double))run3 {

        

        return  ^ (double distance) {

          

            NSLog(@" %f米", distance);

            

            return self;

        };

    }

    以上写法就可以这样来调用

    dog.run3(500).eat3(@"骨头").run3(1000).eat3(@"便便");


    写到这里相信大家已经发现,masonry框架就是采用这种编程思想;


    下面再来说说响应式编程思想,响应式编程就是一种面向数据流和变化传播的编程范式。就是说可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

    说人话就是,你在view中修改数据,model中会跟着变,在model中修改数据,view显示也跟着变,要实现这种效果,iOS开发中,要想实现这种效果,可以使用KVO,但是!苹果的KVO会统一调用同一个方法,且方法固定不变,当要监听的对象过多,十分难以管理维护;



    说到这里,答案已经明了,RAC之所以叫函数响应式编程框架,就是因为她包含了函数式编程思想,和响应式编程思想;

    好了,时间不早,洗洗睡觉,至于RAC的具体用法和实现,请期待下回分解!


    展开全文
  • 浅谈函数式编程和函数响应式编程链接: https://segmentfault.com/a/1190000003632186 作者: Fakefish全文转载如下:大概几个月前,受题叶影响开始关注函数式编程,在看完一本 js 的函数式之后,对函数式有了点基本的...
  • 函数响应式编程(FRP)思想

    千次阅读 2017-03-14 20:24:55
    ReactiveCocoa是IOS广为使用的技术框架,而ReactiveCocoa的核心思想就FRP。FRP不同于JAVA的object-oriented和AOP,FRP能让你的代码像数学一样简洁,业务像...函数响应式编程响应式编程思想为体,函数式编程思想为用。
  • 一、简述对于现在很多开发者来说,函数响应式编程已经成为一个必不可少的方式,是面向计算的抽象,将计算描述一种表达式的求职,响应式编程是一种面向数据流的编程范式,与数据的更新相关,把函数式编程里的思路和...
  •  函数响应式编程是两个声明式编程的子范式(函数式 + 响应式)的组合。 (1)函数式编程 函数式编程是一种编程范式,实际上是如何编写程序的方法论。具有五个鲜明的特点 1)函数是“一等公民”。函数与其他数据...
  • TDFReactive 一个基于函数响应式编程和离散模型的API请求库
  • ReactiveObjC (前身是 ReactiveCocoa 或者 RAC) 是一个 Objective-C 框架,实现了函数响应式编程模式。
  • 函数式编程 -> 函数响应式编程

    千次阅读 2020-12-04 16:09:53
    现在大家已经了解我们是如何运用函数式编程来操作序列的。其实我们可以把这种操作序列的方式再升华一下。例如,你可以把一个按钮的点击事件看作是一个序列: // 假设用户在进入页面到离开页面期间,总共点击按钮 3 ...
  • ReactiveCocoa(其简称为RAC)是函数响应式编程框架。RAC具有函数式编程和响应式编程的特性。它主要吸取了.Net的 Reactive Extensions的设计和实现。
  • 函数响应式编程函数响应式编程是一种编程范式,可以极大的简化项目,特别是处理嵌套回调的异步时间、复杂的列表过滤和变换或者时间的相关问题.而RxJava就是函数响应式编程的重要框架。RxJava简述我们在Github上搜索...
  •  今天呢,讨论一下关于ReactiveCocoa,这个采用函数响应式编程(FRP)的框架,以下会对ReactiveCocoa简称为RAC。  之前看过一遍文章,说的是作为一个iOS开发者,写的每一行代码几乎都是在相应某个事件,例如按钮的...
  • ReactiveCocoa(其简称为RAC)是函数响应式编程框架。RAC具有函数式编程和响应式编程的特性。它主要吸取了.Net的 Reactive Extensions的设计和实现。 函数式编程 (Functional Programming) 函数式编程也可以写
  • 函数响应式编程(FRP)为解决现代编程问题提供了全新的视角。一旦理解它,可以极大地简化你的项目,特别是处理嵌套回调的异步事件,复杂的列表过滤和变换,或者时间相关问题。
  • 声明式编程 声明式编程(declarative programming)是一种编程范型,与命令式编程相对立。它描述目标的性质,让电脑明白... 声明式语言包括数据库查询语言(SQL,XQuery),正则表达式,逻辑编程,函数式编程和组态管
  • disposed.dispose()

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 100,251
精华内容 40,100
关键字:

函数响应式编程