上篇博客我们聊了Signal的几种状态、SignalObserver的关联方式以及Signal是如何向关联的Observer发送事件的。本篇博客继续上篇博客的内容,来聊一下Signal类中静态的neverempty计算属性以及pipe()静态方法。然后再聊聊Signal中的面向协议编程中的面向协议扩展。在Signal中,只要是对Signal的扩展都是加在了Signal所实现的协议中,稍后会进行介绍。

一、Signal中获取实例的静态计算属性

在本篇博客的第一部分我们先来看看Signal类中的两个属性,一个是never,另一个是empty。之所以将这两个计算属性放在一块,是因为这两个静态计算属性都是用来获取Signal实例的。但是所获取实例的功能不同。解析来我们就来看一下never以及empty的实现和使用方式。

1. never

下方是never是代码实现,实现比较简单。其中就是调用了Signal的构造器,但是没有在构造器的尾随闭包中做任何事情。通过该计算属性获取的Signal对象,不会获取到内置的Observer对象,从而Signal的对象持有者是不能对Signal所关联的观察者发送事件的。

  

下方是ReactiveSwift官方关于never的测试用例以及输出结果。从运行结果中来看,所关联的Observer对象是不会收到来自Signal的任何消息的。

  

2、empty

聊完never,接下来我们来看一下Signal的静态计算属性empty的实现以及执行方式。empty的使用方式与never差不多,empty形式的Signal在管理Observer时,只会执行该Observer的interrupted事件。解析下来我们就来看一下empty的实现方式。

我们先看一下empty的使用方式,下方这段代码就是ReactiveSwift官方的empty使用的示例,以及该示例的输出结果。我们从Signal的静态计算属性empty中获取Signal是实例。然后在关联Observer时,都会执行Observer的interrupted事件的闭包体。

  

接下来我们来看一下empty的实现方式,代码比较简单,比never就多了一个observer.sendCompleted()方法。在之前我们聊Observer时,我们知道sendCompleted()就是执行观察者的Event.completed事件。sendComplented()会执行Observer中的Action,并带有.completed事件。

  

下方这个代码段中Observer初始化时的尾随闭包,就是observer.sendComplented()方法所执行的内容。而在这个尾随闭包中,我们看到有一个event.isTerminating的判断,当是.failed、.completed 和 .interrupted事件时event.isTerminating的值都是true。所以该if块中就是empty要执行的方法。

在if语句块中,核心的内容就是修改当前Single的SignalState。当调用Signal的构造器时,SignalState默认是SignalState.alive(AliveState()),而if语句块中就负责将该状态修改成TerminatingState状态,如下所示。

在关联Observer时,会用到TerminatingState状态,下方会给出介绍。

  

下方代码段就是Signal关联Observer是所调用的方法。从下方代码不难看出,当Signal处于非活跃状态.alive时,token的值就是nil,当token未赋值时,就会执行所关联对象Observer的sendInterrupted()方法,向所关联的Observer发送.interrupted事件,从而执行Observerinterrupted的闭包体。至此,empty的相关内容就解析完了。

  

二、Signal的静态方法pipe()

Signal中的静态方法pipe()本质上就是一个便利构造器,该便利构造器返回的参数是一个元组,其不仅仅返回一个Signal的实例,而且返回Signal用于发送事件的内置observer对象。pipe()是获取Signal实例的主要方式,接下来我们就来看一下pipe的使用方式以及pipe()的内部实现。

1、pipe()的使用示例

下方这个pipe()的应用示例是从官网Demo的基础上修改的,下方是对该段代码的介绍:

  • 首先通过Signal的pipe()静态方法可以获取一个Signal实例以及该实例所持有的Observer对象,也就下方元组中的(signal, sendMessage)。

  • 紧接着我们创建两个Observer对象,并且给出Value事件所执行的闭包体。我们将这两个Observer的实例命名为subscriber1和subscriber2。

  • 然后我们将subscriber1添加到signal中,在signal调用observe()方法添加Observer时,会返回一个ActionDisposable类型的对象,我们可以使用该对象移除观察者。添加完毕后,我们就调用sendMessage的send(value)方法,发送value事件,然后观察者subscriber1所对应的Value事件闭包体就会得到执行。

  • 我们以同样的方将subscriber2添加到Signal中,然后通过是调用sendMessage的send(value)方法,发送value事件。我们就可以看到subscriber1和subscriber2这两个观察者的Value闭包就会执行。

  • 然后我们调用actionDisposable对象的dispose()方法,将subscriber1从Signal的Bag中移除掉。移除后subscriber1就不会收到来自Signal的事件了。actionDisposable对象的dispose()方法稍后会介绍到。

  • 我们再次调用sendMessage的send(value)方法,subscriber1的Value事件的闭包就不会被执行。

  

2、pipe()的代码实现

pipe()的代码实现比较简单,就是通过元组将Signal的对象以及Observer的对象进行返回即可,下方就是pipe()的代码实现。因其实现比较简单,在此就不做过多赘述了。

  

3、ActionDisposable的代码实现

接下来我们来解析一下ActionDisposable的代码实现,在每次观察者Observer与Signal调用observe()方法进行关联时都会返回一个ActionDisposable对象,该对象可以是对应的观察者取消对Signal信号的观察。接下来我们就来看一下ActionDisposable的具体实现。

下方就是ActionDisposable类的实现,ActionDisposable的代码实现比较简单,本质上就是通过构造器接收一个闭包,然后将这个尾随闭包赋值给action变量,然后在dispose()方法中去调用该action闭包。

  

下方就是在关联Observer和Signal的observe()方法中实例化ActionDisposable的相关代码。下方的大红框中就是ActionDisposable方法中action的闭包体,也是dispose()方法中主要执行的代码。在改闭包中所执行的目的也是比较单一的,其中主要做了一件事情,就是根据token从Signal活跃状态的Bag中移除相应的Observer,换句话说就是移除观察者

  

 三、Signal的可扩展性

在本篇博客的最后一部分,想聊一下Signal的可扩展性设计。对Signal功能的扩展,主要使用了面向协议扩展的形式。主要就是是Signal实现SignalProtocol,然后我们对 SignalProtocol这个协议进行扩展,而不是对Signal这个类本身进行扩展。所以此处我们称之为“面向协议扩展”,对SignalProtocol这个协议进行扩展后,因为Signal这个类遵循SignalProtocol,所以Signal也会拥有SignalProtocol所扩展的功能。

下方截图中就是SignalProtocol的实现以及相应的扩展。从下方代码中我们可以看到,Signal类的大部分核心功能都是通过SignalProtocol的协议扩展而拥有的。SignalProtocol有好多扩展,本篇博客就不细说了,下篇博客我们找一些比较核心的SignalProtocol的扩展拿出来聊聊。

  

今天博客就先到这儿,下篇博客我们会对ReactiveSwift中的SignalProtocol的延展的实现进行介绍。

上述代码github分享地址:https://github.com/lizelu/TipSwiftForRac

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px "PingFang SC"; color: #4bd156 }
span.s1 { }
span.s2 { font: 14.0px Menlo }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #4bd156 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #ffffff }
span.s1 { }

ReactiveCocoa源码解析(四) Signal中的静态属性静态方法以及面向协议扩展的更多相关文章

  1. ReactiveSwift源码解析(四) Signal中的静态属性静态方法以及面向协议扩展

    上篇博客我们聊了Signal的几种状态.Signal与Observer的关联方式以及Signal是如何向关联的Observer发送事件的.本篇博客继续上篇博客的内容,来聊一下Signal类中静态的ne ...

  2. ReactiveCocoa源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  3. ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现

    上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...

  4. Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?

    Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的?   如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...

  5. Sentinel源码解析四(流控策略和流控效果)

    引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...

  6. Spring5源码解析-Spring框架中的单例和原型bean

    Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...

  7. ReactiveSwift源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  8. Dubbo 源码解析四 —— 负载均衡LoadBalance

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...

  9. iOS即时通讯之CocoaAsyncSocket源码解析四

    原文 前言: 本文为CocoaAsyncSocket源码系列中第二篇:Read篇,将重点涉及该框架是如何利用缓冲区对数据进行读取.以及各种情况下的数据包处理,其中还包括普通的.和基于TLS的不同读取操 ...

随机推荐

  1. Java过滤敏感词语/词汇---DFA算法

    最近网站需要在评论.投稿等地方过滤敏感词汇,于是在网上查找了相关教程,特此整理分享. 关于DFA算法,详细的可以去http://blog.csdn.net/u013378306/article/det ...

  2. 微信小程序,前端大梦想(七)

    微信小程序之数据缓存实例-备忘录 数据缓存在移动端的使用是非常重要的,既可以减少用户的流量支出又可以提高程序的访问速度和用户体验.每个微信小程序都可以有自己的本地缓存,可以通过 wx.setStora ...

  3. Tomcat8-源码编译及开发

    前言 下载Tomcat8源码进行分析,最好的方式,可以编译及运行,从网上查询了很多方式,总是不能完整的运行,由于本人采用idea编辑器,所以喜欢maven的方式,所以综合了网上的多种方案,最终可以在i ...

  4. DOM4J介绍与代码示例

    DOM4J是dom4j.org出品的一个开源XML解析包.Dom4j是一个易用的.开源的库,用于XML,XPath和XSLT.它应用于Java平台,采用了Java集合框架并完全支持DOM,SAX和JA ...

  5. 开涛spring3(6.5) - AOP 之 6.5 AspectJ切入点语法详解

    6.5.1  Spring AOP支持的AspectJ切入点指示符 切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的Aspect ...

  6. java虚拟机学习-JVM调优总结-基本垃圾回收算法(7)

    可以从不同的的角度去划分垃圾回收算法: 1.按照基本回收策略分 引用计数(Reference Counting): 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计 ...

  7. 走进javascript——数组的那些事

    Array构造器 如果参数只有一个并且是Number类型,那么就是指定数组的长度,但不能是NaN,如果是多个会被当做参数列表. new Array(12) // (12) [undefined × 1 ...

  8. Error getting nested result map values for 'company'. Cause: java.sql.SQLException: Invalid value for getInt() - 'NFHK188'

    我今天遇到一个我不解的问题,是mybatis多对一关系查询出问题了,但是我自己还是解决了,在网上也查过那个错误,可是找不到我想要的.不知道你们遇到过没有,我接下来分享给大家.希望我这个第一篇博客能帮助 ...

  9. TCP:三次握手、四次握手、backlog及其他

    TCP是什么 首先看一下OSI七层模型: 然后数据从应用层发下来,会在每一层都加上头部信息进行封装,然后再发送到数据接收端,这个基本的流程中每个数据都会经过数据的封装和解封的过程,流程如下图所示: 在 ...

  10. SQL数据库的多表查询

    多表查询分为 内.外连接 外连接分为左连接(left join 或left outer join).右连接(right join 或者 right outer join).和完整外部连接 (full ...