ReactiveCocoa框架菜鸟入门——信号(Signal)详解

基础知识

在阅读本文之前,请确保你已成功导入ReactiveCocoa框架并对信号(Signal)和订阅者(Subscriber)有基本了解。或者尝试着完全理解以下一段内容:

信号是数据流,可以被绑定和传递。可以把信号想象成水龙头,只不过里面不是水,而是玻璃球(value),直径跟水管的内径一样,这样就能保证玻璃球是依次排列,不会出现并排的情况(数据都是线性处理的,不会出现并发情况)。水龙头的开关默认是关的,除非有了接收方(subscriber),才会打开。这样只要有新的玻璃球进来,就会自动传送给接收方。接收方就是放在水龙头下的盆子,对于水龙头不同的出水状况,有自己的处理方式。水龙头出水时会通知下方的水盆,如果没有水盆,水龙头始终处于关闭状态。

(转自linyawen的博客,附加了一些个人总结。)

再看信号

在前文中,以UITextfield的信号为例,示范了信号(Signal)的基本使用。但是,显然信号(Signal)的功能远不止这些。本文将详细介绍对信号进行的一系列操作。

首先,作为一个信号,我们关注它的两个方面:

  1. 处理逻辑
  2. 数据内容

处理逻辑指的是创建信号的时候,它是如何通知订阅者(Subscriber)并选择发送何种事件的。数据内容指的是信号会传递给订阅者(Subscriber)什么样的数据。这就像一个水龙头,它什么时候告诉水盆自己正在滴水,或是已经滴完水了。以及它把什么丢入水盆,是原始的水滴,还是水滴的质量?

如果我们需要对这些内容进行自定义的修改,那么修改原信号显然是不可行的(信号已经被创建了)。因此,这就牵涉到信号之间的转换(Map)与组合(Combine)。

我们从RACSignal类最基础的方法开始讨论信号之间的转换(Map)与组合(Combine)。对于每一个方法,我们需要关注这个方法的功能、返回值类型。由于ReactiveCocoa大量使用了block,还需要关注方法中block的参数类型和返回值类型。

绑定(Bind)

在RACSignal.m中找到bind方法。官方定义如下:

/*

* -bind: should:

*

* 1. Subscribe to the original signal of values.

* 2. Any time the original signal sends a value, transform it using the binding block.

* 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they’re received.

* 4. If the binding block asks the bind to terminate, complete the original signal.

* 5. When all signals complete, send completed to the subscriber.

*

* If any signal sends an error at any point, send that to the subscriber.

*/

观察bind方法的实现(太长了,就不贴出来了)显然这个方法返回了一个新的信号。定义4、5告诉我们新的信号发送事件和原始信号是同步的。这是bind方法最重要的特点之一。

同样需要注意的是方法的第一行代码:

RACStreamBindBlock bindingBlock = block();

这里的block不再是此前我们简单认为的block。这个block被调用后,才得到一个block。这是一个block的嵌套。注意到后半段一行代码:

id signal = bindingBlock(x, &stop);

这里表明,通过解封出来的block,产生一个新的signal。

因此,bind方法的作用大概已经清楚了:通过传入一个block(解除一层嵌套后)作用于原始信号上,产生一个新的信号。这个新的信号与原始信号保持同步。

FlattenMap

没办法翻译这个方法。但是它确实信号非常重要的一个方法。网上很多教程先介绍Map方法再把FlattenMap作为Map的补充介绍,这个逻辑是不正确的。观察源码不难发现,绑定(Bind)属于最底层操作,核心是保持了新旧信号的同步性和创造了对原始信号进行修改的可能。而FlattenMap初步提供了修改的机制。

观察FlattenMap方法的实现代码:

- (instancetype)flattenMap:(RACStream * (^)(id value))block {
    Class class = self.class;

    return [[self bind:^{
        return ^(id value, BOOL *stop) {
            id stream = block(value) ?: [class empty];
            NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);

            return stream;
        };
    }] setNameWithFormat:@"[%@] -flattenMap:", self.name];
}

最后的setNameWithFormat显然是一个格式化输出,并不影响信号的本质。直接无视它,那么这个方法的核心其实就是return [self bind:^{}];即返回了一个绑定了原始信号的新信号。bind方法的block中的return方法将在RACStreamBindBlock bindingBlock = block();时被调用,相当于解除了嵌套。实际上作用于原始信号的代码就是被return的那个block中的代码。

因此不难看出,FlattenMap方法的作用在于通过传入一个block,作用在原始信号传出的value上,得到一个新的信号。很抱歉我不明白一个blcok作用在value上能得到什么新的信号,但是实际使用中的情况是,这个value作为参数被传入blcok中,但是block完全没有用到这个参数,而是自己创建了一个信号。

相比于绑定(Bind)侧重于新信号和原始信号的同步性,FlattenMap方法实现了新 信号的修改。绑定(Bind)属于最底层操作,而FlattenMap方法是中间层,为实际应用提供了一个接口。当然有时候FlattenMap方法也会被我们直接调用。

信号(Signal)的各种操作

在之前的基础上,ReactiveCocoa提供了对信号的各种操作。这些操作几乎都用到了FlattenMap方法。意味着返回一个被修改之后的信号。同时,几乎每个操作还调用了return方法。

//这个return不是我们用于返回一个值的return,只是名字比较像。
+ (RACSignal *)return:(id)value {
    return [RACReturnSignal return:value];
}

这个方法涉及到的代码比较多,就不一一细讲。该方法的主要作用是,返回一个新的信号,不过原始信号发送事件时的value将被新的value替换。

有了对绑定(Bind)方法、FlattenMap方法和return方法的理解,基本上就可以通过自己阅读源码搞定对信号(Signal)的各种操作了。这里列出几个常用的操作,如果依然不能理解,或者想要了解更多操作还是建议直接阅读源码。

filter

filter方法返回一个新的signal。原始信号的value被替换为了符合要求的value,从而实现了筛选、过滤的目的。是否符合要求是由传入的block决定的。即原来的信号的value,如果传入block中返回YES,则新的信号也将输出这个value。

map

map方法返回一个新的signal。原始信号的value被替换为了经过block处理的value。

distinctUntilChanged

distinctUntilChanged方法返回一个新的signal。这个signal只在value和前一个value不同的时候才会发送事件。简记为求异存同。

ignore

这个方法需要传入一个value,当信号收到一个value时,会检查是否和传入的value相同,如果相同就不会发送事件给订阅者。

skip & take

顾名思义,就是跳过(只发送)前n条数据。这里的n就是传入的参数值。

doNext

创建一个新的信号,这个信号和原始信号一模一样,不过可以在创建的过程中调用传入的block。

combineLatest:reduce

合并若干个信号,得到一个新的信号。把那些信号的value进行处理,得到一个处理过后的value作为新的信号的value。

通过对信号的各种操作,我们把若干个水龙头连在一起,形成了一个水管。filter像是在两个水龙头之间加了一个过滤网,只有经过过滤网的水才能出现在下一个水龙头里。map像是在水龙头间加了一个转换器,前一个水龙头流出的水经过这个转换器就变成石油了。combineLatest:reduce则是把若干个水龙头的水一起引入一个新的水龙头……

以上是常用的信号(Signal)操作,更多的操作可以在源代码中找到,相信有了之前的基础,看懂这些代码并不困难。现在我们已经有了足够多的办法处理一个信号,开始实际编程工作已经不是问题了。

时间: 2024-10-13 17:31:12

ReactiveCocoa框架菜鸟入门——信号(Signal)详解的相关文章

ReactiveCocoa框架菜鸟入门——信号(Signal)与订阅者(Subscriber)

上一篇文章已经简单的介绍了ReactiveCocoa框架的思想和优势.本文初步研究一下ReactiveCocoa框架的使用方法. 写在开始前 传统的编程思想,大概是用户产生某个事件,然后得到相应的参数,传入事先已经实现的方法中,处理完成后把结果在UI界面上反馈出来.ReactiveCocoa框架中大量的使用了block,这意味着,很多block内的代码,是在将来某一个合适的时刻被执行的.如果你看到block里某个参数并没有被赋值,也没有传入参数,不要奇怪,程序运行到这里的时候还不会执行这个blo

《Linux菜鸟入门》进程详解

●进程 1.进程定义 进程就是cpu未完成的工作 2.ps命令 ps  a    关于当前环境的所有进程,不是自己的也会显示 x    与当前环境无关的所有进程 f    显示进程从属关系 e    显示当前用户环境中的所有进程 l    长列表显示进程的详细信息 u    显示进程的用户信息 ps ax -o %cpu,%mem,user,group,comm,nice  指定显示进程的某些信息 %cpu   负载 mem   内存负载 user   用户 group    组 comm   

Object-C 入门 Xcode 环境详解 HelloWorld 程序

作者 : 韩曙亮 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/38424965 一. Xcode 环境安装 与 工程创建 1. 下载环境 相关资源下载 : -- IOS 相关资料下载页面 : https://developer.apple.com/devcenter/ios/index.action ; -- Xcode 下载页面 : https://developer.apple.com/xcode/downloads/

LINUX 信号概念详解

LINUX 信号概念详解 我们运行如下命令,可看到Linux支持的信号列表: # kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD 18) SIGCONT 19) SIGSTOP

Spring整合JUnit框架进行单元测试代码使用详解

[转]Spring整合JUnit框架进行单元测试代码使用详解 转自 http://blog.csdn.net/yaerfeng/article/details/25187775 感谢博主 :云淡风轻 .仅此一抹 一.Spring提供的JUnit框架扩展: 1. AbstractSpringContextTests:spring中使用spring上下文测试的Junit扩展类,我们一般不会使用这个类来进行单元测试,它是spring内部设计使用到的类    2. AbstractDependencyI

Node.js开发入门—Stream用法详解

Stream是Node.js中非常重要的一个模块,应用广泛.一个流是一个具备了可读.可写或既可读又可写能力的接口,通过这些接口,我们可以和磁盘文件.套接字.HTTP请求来交互,实现数据从一个地方流动到另一个地方的功能. 所有的流都实现了EventEmitter的接口,具备事件能力,通过发射事件来反馈流的状态.比如有错误发生时会发射"error"事件,有数据可被读取时发射"data"事件.这样我们就可以注册监听器来处理某个事件,达到我们的目的. Node.js定义了R

经典Spring入门基础教程详解

经典Spring入门基础教程详解 https://pan.baidu.com/s/1c016cI#list/path=%2Fsharelink2319398594-201713320584085%2F%E7%BB%8F%E5%85%B8Spring%E5%85%A5%E9%97%A8%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B%E8%AF%A6%E8%A7%A3&parentPath=%2Fsharelink2319398594-201713320584085 博达远

【框架】[Hibernate]构架知识点详解入门与测试实例

转载请注明出处:http://blog.csdn.net/qq_26525215 本文源自[大学之旅_谙忆的博客] Hibernate介绍: Hibernate是一个开放源码的.非常优秀.成熟的O/R Mapping框架.它提供了强大.高性能的Java对象和关系数据的持久化和查询功能. O/R Mapping 对象关系映射(Object Relational Mapping,简称ORM)技术,是通过使用描述对象和数据库之间映射的元数据,将Java程序中的对象自动持久化到关系数据库中. 对象和关系

【框架】[Spring3]下载安装、开源框架与IoC控制反转详解

转载请注明出处:http://blog.csdn.net/qq_26525215 本文源自[大学之旅_谙忆的博客] 昨天刚刚初学Spring3,也许Spring3有点老了哈,不过还是先把3学了再去学习4吧,首先先介绍一下如何去下载Spring的必须包吧. (本篇博客适用于初学Spring的朋友) java spring4现在不推荐使用xml配置文件- 当然啦,这些知识点在Spring4还是可以用的. 不过我在这里详解的还是Spring3哈,见谅~ 下载SpringJAR包/文档: Spring官