ReactiveCocoa源码解析(二) Bag容器的代码实现
今天博客我接着上篇博客的内容来,上篇博客我们详细的看了ReactiveSwift中的Observer已经Event的代码实现。接下来我们来看一下ReactiveSwift中的结构体Bag的实现。Bag:袋子,顾明思议,就是用来装东西的,我们暂且将Bag称之为容器。在ReactiveSwift中的Bag主要是用来存储Signal对象的,我们在后期介绍ReactiveSwift源码时会陆陆续续的看到Bag的身影。
因为Bag这个结构体在ReactiveSwift中比较独立,所以我们本篇博客就来聊一下Bag的具体实现。本篇博客我们会详细的介绍Bag的代码实现,并从Bag代码实现中看一下Swift语言本身的东西,并给出Bag的测试用例。当然,本篇博客我们还会涉及到“迭代器模式”,关于“迭代器模式”更详细的信息,请移步于之前发布的关于设计模式的博客《设计模式(十):从电影院中认识"迭代器模式"(Iterator Pattern)》。
一、ContiguousArray
在博客的第一部分我们先来看一下ContiguousArray的相关内容。因为结构体Bag就是在ContiguousArray的基础上进行封装的,也就是说袋子中的元素最终是存放在ContiguousArray中的。在Swift中ContiguousArray与Array的用法差不多,下方是官方对ContiguousArray的介绍。
从下方我们可以清楚的知道ContiguousArray、Array还有ArraySlice的大部分属性和方法是共用的。但是在存储Class或者@objc 协议时,使用ContiguousArray效率会更高一些。但是ContiguousArray不能和Objective-C的NSArray进行桥接,并且不能将ContiguousArray传入到Objective-C的API中。
当然从ContiguousArray名字来看,它是占用连续存储空间的数组。具体请看下方的官方介绍。

二、Bag的基本实现
下方是结构体Bag的基本实现,稍后还会介绍Bag的延展以及与其关联的BagElement类型。接下来我们来详细的看一下其实现。当然下方截图中的代码实现,是将ReactiveSwift中的英文注释给删了,添加了一些中文注释。这样看着更舒服一些。
1、RemovalToken
首先我们来看一下RemovalToken,以及看一下RemovalToken这个类在Bag结构体中所扮演的角色。从下方代码片段中我们不难看出,RemovalToken是一个空类,中该类的名字我们可以看出,该类的对象是充当Token用的。也就是说该类的对象可以作为Bag中所存储元素的唯一标示符,并且可以用来删除元素使用。
我们知道,每个类的对象都会有一个唯一的HashValue。其实在Bag中真正使用到的是RemovalToken的对象所对应的HashValue,这个稍后我们会聊到。
2.Bag的基本实现
从下方代码段中,我们可以看出Bag是以结构体的形式存在的,而且后边紧跟了一个Element的泛型类型。紧接着是类型为 ContiguousArray<BagElement<Element>> 的泛型数组,BagElement<Element>这个类型稍后会提到。
insert()方法负责插入元素,从代码实现来看其实就是向elements数组后方append元素,添加的元素类型为BagElement。inser()方法由@discardableResult进行修饰,说明insert()方法所返回的值可以被忽略,也就是说如果没有变量来接收insert()的返回值的话,程序并不会报出警告。而insert()前方的 mutating关键字一般用来修饰Swift中的枚举或者结构体中的方法,被mutating关键字修饰的方法就可以修改枚举或者结构体中的属性了。用法如下所示。
接下来我们来看一下remove()方法,该方法的参数是一个token,其功能就是通过token来删除元素。当然具体代码实现也是比较简单的,就是对elements数组进行遍历,找到元素的token与传入的token一致的话,我们就将其删除。具体实现如下所示。

三、BagElement结构体的实现
接下来,我们来看一下Bag中所存储元素的类型BagElement的实现,代码如下所示。当然实现比较简单,BagElement也是一个泛型结构体,其泛型类型Value其实就是Bag的泛型类型Element。其中有两个属性,一个Value,用来存储值。另一个是token,用来存储该值对应的唯一标示。
紧接着是BagElement的的延展,用来输出描述信息的,如下所示。

四、Bag的延展
接下来我们来看一下Bag的延展,代码如下所示。Bag的延展中的相关内容还是比较简单的。首先定义了一个Array<Element>.Index的类型别名Index,其实就是Int类型。然后是startIndex和endIndex两个计算属性,用来获取Bag的第一个元素的索引,和结束位置的索引。
subscript()方法是为Bag结构体添加自定义下标,使其支持下标访问元素的形式。makeIterator()方法则用来创建Bag<Element>所对应的迭代器。关于Bag的迭代器,稍后会进行介绍。

五、Bag的迭代器
接下来我我们就来看一下Bag容器的迭代器,其实就是“迭代器模式”的具体应用。下方代码段就是Bag的迭代器的具体实现。从下方代码我们不难看出,BagIterator实现了Swift中的迭代器协议IteratorProtocol,然后给出了迭代器的next()方法的实现。下方我们将会对该迭代器进行测试。

六、Bag的测试用例
下方代码片段中是对Bag的测试用例。首先我们初始化了一个Bag实例,然后指定其泛型类型为String。紧接着我们又创建了一个bagsTokens的数组,用来存储myBags中每个元素所对应的token,便于在移除元素时使用。最后是往myBags中添加值了。每添加一个值我们就记录一下该值所对应的token。
在添加完元素后,我们可以遍历输出一下每个token对象的HashValue。然后我们可以通过token来移除myBags中的元素。
最后我们可以从myBags中获取相应的迭代器,然后使用迭代器访问myBags中的元素。

下方是对Bag中的Token以及Bag中的所有元素进行的输出,如下所示:

今天博客就先到这儿,下篇博客会继续更新ReactiveSwift相关的东西。
上述代码github分享地址:https://github.com/lizelu/TipSwiftForRac
ReactiveCocoa源码解析(二) Bag容器的代码实现的更多相关文章
- ReactiveSwift源码解析(二) Bag容器的代码实现
今天博客我接着上篇博客的内容来,上篇博客我们详细的看了ReactiveSwift中的Observer已经Event的代码实现.接下来我们来看一下ReactiveSwift中的结构体Bag的实现.Bag ...
- ReactiveCocoa源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...
- Mybatis源码解析(二) —— 加载 Configuration
Mybatis源码解析(二) -- 加载 Configuration 正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...
- RxJava2源码解析(二)
title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...
- Sentinel源码解析二(Slot总览)
写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...
- ReactiveSwift源码解析(一) Event与Observer代码实现
ReactiveCocoa这个框架是做什么用的本篇博客就不做过多赘述了,什么是"响应式编程"也不多聊了,自行Google吧.本篇博客的主题是解析ReactiveCocoa框架中的核 ...
- vueJs 源码解析 (三) 具体代码
vueJs 源码解析 (三) 具体代码 在之前的文章中提到了 vuejs 源码中的 架构部分,以及 谈论到了 vue 源码三要素 vm.compiler.watcher 这三要素,那么今天我们就从这三 ...
- ReactiveCocoa源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- Spring5源码解析_IOC之容器的基本实现
前言: 在分析源码之前,我们简单回顾一下SPring核心功能的简单使用: 容器的基本用法 Bean是Spring最核心的东西,Spring就像是一个大水桶,而Bean就是水桶中的水,水桶脱离了水就没有 ...
随机推荐
- JavaScript中undefined 和not defined
首先呢,我们来介绍undefined,xx is not defined的区别 (创建一个html文件,在头部编写JavaScript代码) 我们先编写如下代码: <script type=&q ...
- 点击滚动图片JS部分代码以及css设置注意事项
下面js代码可以实现8张图片点击左右按钮后切换的过渡动画效果 var pslul11=document.getElementById('pslul11')var pslspan1=document.g ...
- JavaScript异步编程
前言 如果你有志于成为一个优秀的前端工程师,或是想要深入学习JavaScript,异步编程是必不可少的一个知识点,这也是区分初级,中级或高级前端的依据之一.如果你对异步编程没有太清晰的概念,那么我建议 ...
- 【网站管理1】_dede织梦后台如何发布文章
对于新手可能不了解,dede织梦后台是如何发文章的.下面说下我的经验. 发布文章步骤 1.打开浏览器,推介谷歌,360极速浏览器,火狐浏览器,输入网站后台网址 出现如下图登入界面 2.输入账号密码 ...
- foreach底层机制
简单例子 直接了解foreach底层有些困难,我们需要从更简单的例子着手.下面上一个简单例子: public class Simple { public static void main(String ...
- 什么是mybatis 为什么要使用my batis
1.定义MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.2.使用原因MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用 ...
- Jetty + HttpClient 处理http请求
本人最近通过自己动手处理http请求,对http协议.Jetty以及HttpClient有了更深刻的理解,特在此与大家分享. 此图是http协议的请求格式.根据请求方法,有get和post之分.get ...
- Hibernate学习笔记二:Hibernate缓存策略详解
一:为什么使用Hibernate缓存: Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序访问物理数据库的频次,从而提高应用程序的性能. 缓存内的数据是对物理数据源的复制,应用 ...
- 18、面向对象基本原则及UML类图简介
18.1.面向对象基本原则 18.1.1.面向抽象原则 抽象类特点: a.抽象类中可以有abstract方法,也可以有非abstract方法. b.抽象类不能用new运算符创建对象. c.如果一个非抽 ...
- 将非官方扩展程序加入chrome的白名单
参考来源:http://xclient.info/a/1ddd2a3a-d34b-b568-c0d0-c31a95f0b309.html com.google.Chrome.mobileconfig ...