今天博客我接着上篇博客的内容来,上篇博客我们详细的看了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



ReactiveSwift源码解析(二) Bag容器的代码实现的更多相关文章

  1. ReactiveCocoa源码解析(二) Bag容器的代码实现

    今天博客我接着上篇博客的内容来,上篇博客我们详细的看了ReactiveSwift中的Observer已经Event的代码实现.接下来我们来看一下ReactiveSwift中的结构体Bag的实现.Bag ...

  2. ReactiveSwift源码解析(一) Event与Observer代码实现

    ReactiveCocoa这个框架是做什么用的本篇博客就不做过多赘述了,什么是"响应式编程"也不多聊了,自行Google吧.本篇博客的主题是解析ReactiveCocoa框架中的核 ...

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

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

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

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

  5. Mybatis源码解析(二) —— 加载 Configuration

    Mybatis源码解析(二) -- 加载 Configuration    正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...

  6. RxJava2源码解析(二)

    title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...

  7. Sentinel源码解析二(Slot总览)

    写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...

  8. vueJs 源码解析 (三) 具体代码

    vueJs 源码解析 (三) 具体代码 在之前的文章中提到了 vuejs 源码中的 架构部分,以及 谈论到了 vue 源码三要素 vm.compiler.watcher 这三要素,那么今天我们就从这三 ...

  9. Spring5源码解析_IOC之容器的基本实现

    前言: 在分析源码之前,我们简单回顾一下SPring核心功能的简单使用: 容器的基本用法 Bean是Spring最核心的东西,Spring就像是一个大水桶,而Bean就是水桶中的水,水桶脱离了水就没有 ...

随机推荐

  1. mysql事务隔离级别详解和实战

    A事务做了操作 没有提交 对B事务来说 就等于没做 获取的都是之前的数据 但是 在A事务中查询的话 查到的都是操作之后的数据 没有提交的数据只有自己看得到,并没有update到数据库. 查看InnoD ...

  2. DataSourceBuilder.create().build()

    Spring Boot also provides a utility builder class DataSourceBuilder that can be used to create one o ...

  3. navicat for mysql 破解方法

    https://www.cnblogs.com/da19951208/p/6403607.html  破解教程

  4. 任务调度--使用java.util.Timer实现

    任务调度是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务. 举个例子,比如说我们希望一个系统每周日晚上9点都将数据库文件备份一次,这时我们就可以使用任务调度来实现.为了更加的方便,我们需要 ...

  5. 基于开源 Openfire 聊天服务器 - 开发Openfire聊天记录插件

    原文:http://www.cnblogs.com/hoojo/archive/2013/03/29/openfire_plugin_chatlogs_plugin_.html 随笔-150  评论- ...

  6. php自带验证邮箱 url ip函数

    以前用PHP验证邮箱.URL.IP是否合法都是通过自己写正则来实现,但是有时候脑子发昏,可能会写出一个不是完全正确的正则,导致验证出错,今天发现原来PHP本身自带了验证邮箱.URL.IP是否合法的函数 ...

  7. 在Mybatis-spring上基于注解的数据源实现方案

    一.遇到的痛点 最近在学习Spring-boot过程中,涉及到操作数据库.按照DOC引入mybatis-spring-boot-starter,然后按照套路配置application.properti ...

  8. 【bzoj2432】【NOI2011】兔农

    题目描述 农夫栋栋近年收入不景气,正在他发愁如何能多赚点钱时,他听到隔壁的小 朋友在讨论兔子繁殖的问题. 问题是这样的:第一个月初有一对刚出生的小兔子,经过两个月长大后,这 对兔子从第三个月开始,每个 ...

  9. postman-----使用CSV和Json文件实现批量接口测试

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ".PingFang SC"; color: #454545 } span ...

  10. 前端笔记之Vue(二)组件&案例&props&计算属性

    一.Vue组件(.vue文件) 组件 (Component) 是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素,Vue.js 的编译器 ...