ReactiveCocoa源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见《ReactiveSwift源码解析之Bag容器》。本篇博客我们就来聊一下信号量,也就是Signal的的几种状态以及Signal的基本实现。当然本篇博客所解析的源码会用到上篇博客介绍的Bag容器。本篇博客我们先通过一个示例来看一下Signal是如何工作的,具体说来就是Signal是如何与Observer关联的,来聊一下Observer是如何观察和Signal发出的信号的。
之前我们也详细的聊过Observer和Event相关的东西,详情请参见《ReactiveSwift源码解析之Event与Observer》。本篇博客我们先通过一个实例来看一下Signal和Observer的关系,然后在看一下Signal中的几种状态,最后看一下Signal是如何给Observer发送事件(通知)的。
在聊Signal之前,我们要搞清楚,Signal与Observer的关系是一对多的关系,也就是说Signal是广播的形式往Observer发事件的。这也就是典型的“观察者模式”。
一、Signal的简单使用示例
接下来我们先来看一段Signal的使用示例,虽然Single类中有一些创建Signal对象的便利方法,但是下方Demo中我们采用最原始的Signal创建方式,也就是直接调用Signal的构造器。下方的是对该代码段的介绍:
首先我们创建了一个Observer的对象myObserver, 主要是用来给Signal绑定的观察者来发送事件的。
然后我们调用Signal的构造器创建了一个Signal的对象mySignal。通过该构造器的尾随闭包,我们可以拿到这个负责给Single所绑定的所有观察者发送事件的Observer。然后使用myObserver进行存储。
接着我们创建了两个观察者,也就是Observer的对象subscribe01和subscribe02.
创建好观察者后,就是将这两个观察者与我们的mySignal对象进行绑定了。也就是说subscribe01和subscribe02这两个观察者,观察mySignal发过来的事件。
最后就是调用mySignal中的myObserver进行事件的发送了。
从上述示例的输出结果,我们可以看出,当myObserver发送Value事件时,mySignal的所有Value事件的观察者都可以收到该事件。接下来我我们就来剖析一下Signal与Observer是如何进行绑定的。以及Signal是如何发送事件的。
下面我们可以通过一个图来简单的看一下Signal和Observer的关系。下方画了一个简图来表示这一关系。左边是我们创建的多个Observer对象,然后我们可以将这些对象与Signal对象进行关联,这个关联本质上是将其存放在Signal对象中的Bag中。在Signal对象中也有一个observer对象。Signal的用户可以通过Signal构造函数的尾随闭包来获取到这个内置的observer对象,也即是上述示例的myObserver,使用这个myObserver对象给Bag中所有的Observer发送相应的事件。
在myObserver发送事件的方法中,本质上是对Bag中所有元素的遍历,将myObserver所接受的事件转发给Bag中所有的元素。接下来我们要做的事情就是看其具体的实现方式,当然,具体实现是比下方这张简图的结构要复杂一些。
二、Signal的构造器与observer(observer: Observer)方法
顺藤摸瓜、我们可以从上述实例为切入点来展开对Single实现的解析。在上述实例中我们使用到了Observer,在之前的博客中我们对Observer以及Observer所发送事件Event进行详细的介绍,所以就不对其进行过多赘述了。接下来我们就把重点放在Signal上。
1、Signal的构造器
上述示例初次用到Signal时,是使用的Signal的构造器,接下来我们就来看一下Signal的构造器的实现。下方代码段就是Signal类中的核心属性以及构造方法了。在构造器中我们可以看出是对这些属性进行了初始化。SignalState来标记当前信号量的状态,在Signal中扮演的角色还是比较重要的,稍后会进行介绍。两个NSLock的常量就是两个同步锁,保证原子性操作。
最下方就是初始化了一个Observer的常量,这个常量就负责给Signal所绑定的观察者进行发送事件,通过构造器的尾随闭包回调给Signal的使用这。该observer常量对应这上述实例的myObserver。observer在初始化时,直接调用的是Observer的构造器,尾随闭包就是Observer中Action的闭包体。
2、添加观察者的方法:observer(observer: Observer)
接下来我们在来看示例中添加观察者使用到的observer(observer: Observer)方法。下方就是observer()方法的具体实现。
首先我们来看一下token,token的类型是RemovalToken,RemovalToken在《ReactiveSwift源码解析之Bag容器》中详细的介绍过。主要是用来移除Bag容器中元素的。
然后判断当前的Signal是否是SignalState.alive状态,如果是的话,取出SignalState.alive状态所绑定的值snapshot。此处的snapshot就是一个Bag的对象,用来存储与Signal所关联的所有Observer。然后就是snapshot这个Bag中添加关联的Observer了,关联后,就是更新SignalState.alive所关联的值了。当然为了保证状态更新的原子性操作,这里使用了updateLock。
下方负责初始化Disposable对象,该对象用来将Signal对应的Observer置为失效的状态。本质上就是移除观察者的操作。ActionDisposable中主要做的事情就是从Bag中根据Token移除Observer对象。
3、myObserver.send(value)方法
接下来我们就来看一下在我们实例中myObserver.send(value)方法所做的事情。也就是Signal构造器中对observer对象赋值是尾随闭包中要做的事情。下方就是send(value)方法主要做的事情,如果Signal是活跃状态的话,就会取出该状态值所绑定的Bag对象,Bag中存储的是所有和Signal所关联的Observer,然后遍历Bag中所有的Observer对象,并调用Observer对的Action闭包执行相应的事件。具体做法如下:
三、SignalState解析
SignalState在Signal中所扮演的角色是比较重要的,因为其中的活跃状态.alive就关联着存储所有可以接受信号量事件的观察着的Bag,稍后会进行解析。本部分我们就来详细的看一下SignalState中的内容实现。
1、SignalState枚举的实现
SignalState的代码实现就比较简单了,就是一个枚举。而这个枚举中有三个枚举值,这三个枚举值对应这信号量的三种状态。
alive状态表示信号量处于活跃状态,可以发送事件,alive状态有一个AliveState类型的关联值,稍后会对AliveState进行介绍。
terminating则说明信号量正在被终止,terminating也有一个关联值,该关联值是一个TerminatingState类型的值,下方也会介绍到。
terminated状态说明该信号量处于终止状态,不可在发事件了。
2、AliveState类的实现
下方就是AliveState类的具体实现,AliveState类的主要作用就是与SignalState.alive状态进行关联的。在AliveState中我们可以看到有一个observers的属性,该属性就是Bag容器,其中可存储的类型是Observer。也就是说,在信号量活跃状态下所绑定的观察者都存储在这个Bag中。而retaining属性中存储的就是与Bag中观察者所对应的Signal,从这个类结构中可以看出Signal与Observer是一对多的关系。
3、TerminatingState类的实现
TerminatingState类的实现与AliveState类的实现差不多,下方是TerminatingState类的代码,在该代码实现中也有一个存储着Observer对象的Bag容器。该Bag容器中存储的是该状态下所对应的Observer,而下方的Event类型的event属性则是该状态所对应的事件。如下所示:
今天博客就先到这儿,下篇博客我们继续对ReactiveSwift中的Signal的实现进行介绍。
上述代码github分享地址:https://github.com/lizelu/TipSwiftForRac
ReactiveCocoa源码解析(三) Signal代码的基本实现的更多相关文章
- ReactiveSwift源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...
- ReactiveCocoa源码解析(四) Signal中的静态属性静态方法以及面向协议扩展
上篇博客我们聊了Signal的几种状态.Signal与Observer的关联方式以及Signal是如何向关联的Observer发送事件的.本篇博客继续上篇博客的内容,来聊一下Signal类中静态的ne ...
- Celery 源码解析三: Task 对象的实现
Task 的实现在 Celery 中你会发现有两处,一处位于 celery/app/task.py,这是第一个:第二个位于 celery/task/base.py 中,这是第二个.他们之间是有关系的, ...
- Mybatis源码解析(三) —— Mapper代理类的生成
Mybatis源码解析(三) -- Mapper代理类的生成 在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...
- ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- React的React.createRef()/forwardRef()源码解析(三)
1.refs三种使用用法 1.字符串 1.1 dom节点上使用 获取真实的dom节点 //使用步骤: 1. <input ref="stringRef" /> 2. t ...
- ReactiveCocoa源码解析(二) Bag容器的代码实现
今天博客我接着上篇博客的内容来,上篇博客我们详细的看了ReactiveSwift中的Observer已经Event的代码实现.接下来我们来看一下ReactiveSwift中的结构体Bag的实现.Bag ...
- Hbase flusher源码解析(flush全代码流程解析)
版权声明:本文为博主原创文章,遵循版权协议,转载请附上原文出处链接和本声明. 在介绍HBASE flush源码之前,我们先在逻辑上大体梳理一下,便于后续看代码.flush的整体流程分三个阶段 1.第一 ...
- Spring源码解析 - springMVC核心代码
一.首先来讲解下springMVC的底层工作流程 1.首先我们重点放在前端控制器(DispatcherServlet) 其类图: 因为从流程图看,用户的请求最先到达就是DispatcherServle ...
随机推荐
- Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace 解决方法
这个问题是在用到spring时,本地IDE里面跑的很正常,但是打包后在集群上运行时报错. 多方查找资料后确定了问题的根源,由于在依赖中调用了spring的许多包,会存在文件覆盖的情况. 具体是 这三个 ...
- Telegram学习解析系列(一):认识一下Telegram的源码
前言: Telegram不知道有多少同行听过这玩意,或者在看它的源码.我是出于工作原因才接触到这东西,看的真是的......变方了!一个月估计刚刚找到门,还没进去多深,把自己的心得和对源码的认识以及我 ...
- Android开发之布局--RelativeLayout布局
RelativeLayout 相对布局 true或false属性 Layout_centerHorizontal 当控件位于父控件的横向中间位置 Layout_centerVertical 当 ...
- hdu2571 命运 简单DP
简单dp 状态方程很好想,主要是初始化.... 代码: #include<iostream> #include<cstdlib> #include<cstdio> ...
- Unable open dabase as spfile parameter incorrect
Error desc: ORA-00821: Specified value of sga_target 16M is too small, needs to be at least 4832M ...
- 获取JUnit的执行结果
junit执行之后会有一个结果展示,下面就来看一下怎么获取这些结果并将其存储为一个对象 junit代码如下: package test; import org.junit.After; import ...
- 数据结构与算法系列研究七——图、prim算法、dijkstra算法
图.prim算法.dijkstra算法 1. 图的定义 图(Graph)可以简单表示为G=<V, E>,其中V称为顶点(vertex)集合,E称为边(edge)集合.图论中的图(graph ...
- MHA在线切换的步骤及原理
在日常工作中,会碰到如下的场景,如mysql数据库升级,主服务器硬件升级等,这个时候就需要将写操作切换到另外一台服务器上,那么如何进行在线切换呢?同时,要求切换过程短,对业务的影响比较小. MHA就提 ...
- Android利用文本分割拼接开发一个花藤文字生成
今天研究了一个小软件,挺有意思的,尽管网上已经很多那种软件,但是今天还是在这里给大家分享一下这个软件的具体开发过程 首先,这个软件只需要三个主要控件,EditText.Button以及TextView ...
- Cornerstone 3.0.3 for mac 破解版
破解版本 直接安装即可 解压密码:xclient.info 下载地址: 链接: https://pan.baidu.com/s/1mhD64vY 密码: nwmc