ReactiveSwift源码解析(三) 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
ReactiveSwift源码解析(三) Signal代码的基本实现的更多相关文章
- ReactiveCocoa源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...
- ReactiveSwift源码解析(七) Signal的CombineLatest的代码实现
本篇博客我们就来聊一下combineLatest()的使用以及具体的实现方式.在之前的<iOS开发之ReactiveCocoa下的MVVM>的博客中我们已经聊过combineLatest( ...
- ReactiveSwift源码解析(十) Lifetime代码实现
为了之后博客的进行,本篇博客我们就来聊一下ReactiveSwift框架中的Lifetime类的具体实现.从Lifetime这个名字中我们就这道,就是生命周期.在ReactiveSwift中使用Lif ...
- ReactiveSwift源码解析(四) Signal中的静态属性静态方法以及面向协议扩展
上篇博客我们聊了Signal的几种状态.Signal与Observer的关联方式以及Signal是如何向关联的Observer发送事件的.本篇博客继续上篇博客的内容,来聊一下Signal类中静态的ne ...
- ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- Celery 源码解析三: Task 对象的实现
Task 的实现在 Celery 中你会发现有两处,一处位于 celery/app/task.py,这是第一个:第二个位于 celery/task/base.py 中,这是第二个.他们之间是有关系的, ...
- Mybatis源码解析(三) —— Mapper代理类的生成
Mybatis源码解析(三) -- Mapper代理类的生成 在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...
- ReactiveSwift源码解析(一) Event与Observer代码实现
ReactiveCocoa这个框架是做什么用的本篇博客就不做过多赘述了,什么是"响应式编程"也不多聊了,自行Google吧.本篇博客的主题是解析ReactiveCocoa框架中的核 ...
- ReactiveSwift源码解析(二) Bag容器的代码实现
今天博客我接着上篇博客的内容来,上篇博客我们详细的看了ReactiveSwift中的Observer已经Event的代码实现.接下来我们来看一下ReactiveSwift中的结构体Bag的实现.Bag ...
随机推荐
- Windows上使用Thunderbird与GPG发送和解密公钥加密的电子邮件
作者:荒原之梦 原文链接:http://zhaokaifeng.com/?p=552 非对称加密的原理: 最先出现的加密方法是对称加密.在对称加密算法中是不区分公钥和私钥的,加密与解密使用的都是同一个 ...
- 【SpringMVC】从Fastjson迁移到Jackson,以及对技术选型的反思
为什么要换掉fastjson 直接原因是fastjson无法支持注解形式的自定义序列化和反序列化,虽然其Github上的Wiki上说明是支持的.但是实测结果表明:Test类的序列化被fastjson的 ...
- 基于E-R模型的关系型数据库设计方法
摘要 在管理信息系统开发中,数据库设计的目标是建立DBMS能识别的关系数据模型.而关系数据模型建立的基础是首先建立E-R模型,通过E-R模型才能转换为关系数据模型.如何建立E-R模型以及如何将E-R模 ...
- MQTT, XMPP, WebSockets还是AMQP?泛谈实时通信协议选型 good
Wolfram Hempel 是 deepstreamIO 的联合创始人.deepstreamIO 是一家位于德国的技术创业公司,为移动客户端.及物联网设备提供高性能.安全和可扩展的实时通信服务.文本 ...
- Git常用命令解说
http://blog.csdn.net/hangyuanbiyesheng/article/details/6731629 1. Git概念 1.1. Git库中由三部分组成 Gi ...
- 使用wordpress搭建自己的独立博客
最近想要搭建自己的私人博客, 各种百度,完整的搭建步骤如下! 首先得要有自己的vps或者云主机,我这里是自己的云主机,有自己的域名(我这边目前没有买域名)! 搭建步骤! 1,安装lnmp(linux+ ...
- Java 读书笔记 (九) 运算符
短路逻辑运算符 && 当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了. ...
- bzoj5252 [2018多省省队联测]林克卡特树
斜率优化树形dp?? 我们先将问题转化成在树上选K+1条互不相交路径,使其权值和最大. 然后我们考虑60分的dp,直接维护每个点子树内选了几条路径,然后该点和0/1/2条路径相连 然后我们会发现最后的 ...
- CentOS7 安装Redis 单机版
1,下载Redis4.0.9 进入Redis中文网的下载页面 http://www.redis.cn/download.html 2,上传压缩包到linux系统 cd /user/local/java ...
- appium 元素定位find_element_by_android_uiautomator方法使用
若appium中给定的方法无法满足你的需求,刚好uiautomator中的方法可以满足你的需求时,你可使用find_element_by_android_uiautomator来调用uiautomat ...