ReactiveSwift源码解析(二) 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
ReactiveSwift源码解析(二) Bag容器的代码实现的更多相关文章
- ReactiveCocoa源码解析(二) Bag容器的代码实现
今天博客我接着上篇博客的内容来,上篇博客我们详细的看了ReactiveSwift中的Observer已经Event的代码实现.接下来我们来看一下ReactiveSwift中的结构体Bag的实现.Bag ...
- ReactiveSwift源码解析(一) Event与Observer代码实现
ReactiveCocoa这个框架是做什么用的本篇博客就不做过多赘述了,什么是"响应式编程"也不多聊了,自行Google吧.本篇博客的主题是解析ReactiveCocoa框架中的核 ...
- ReactiveSwift源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...
- ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现
上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...
- Mybatis源码解析(二) —— 加载 Configuration
Mybatis源码解析(二) -- 加载 Configuration 正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...
- RxJava2源码解析(二)
title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...
- Sentinel源码解析二(Slot总览)
写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...
- vueJs 源码解析 (三) 具体代码
vueJs 源码解析 (三) 具体代码 在之前的文章中提到了 vuejs 源码中的 架构部分,以及 谈论到了 vue 源码三要素 vm.compiler.watcher 这三要素,那么今天我们就从这三 ...
- Spring5源码解析_IOC之容器的基本实现
前言: 在分析源码之前,我们简单回顾一下SPring核心功能的简单使用: 容器的基本用法 Bean是Spring最核心的东西,Spring就像是一个大水桶,而Bean就是水桶中的水,水桶脱离了水就没有 ...
随机推荐
- ZooKeeper的使用---命令端
一.进入命令行 ./bin/zkCli.sh 二.常用命令 命令 作用 范例 备注 connect host:port 连接其他zookeeper客户端 connect hadoop2:21 ...
- ajax跨域问题(php)
ajax出现请求跨域错误问题,主要原因就是因为浏览器的"同源策略". 解决方法(我只用过下面这3种): 1. 架设服务器代理:即浏览器请求同源服务器,再由后者请求外部服务(之前博主 ...
- IE下获取XPATH小工具,支持32/64位
背景是曾经友情支持了测试组一小段时间,发现他们使用selenium做页面的自动化测试,需要用到XPath,但IE下没有获取XPath的工具,只能在Firefox和chrome下获取,步骤还比较麻烦.而 ...
- scoring_criteria.go
package types // 评分规则通用接口 type ScoringCriteria interface { // 给一个文档评分,文档排序时先用第一个分值比较,如果 // 分 ...
- 一个Java程序员的2018年展望与2017年总结
回顾2017年,可以说是对我而言有重大转折的一年.我们选择放弃了北京,来到了杭州,开始了新的生活.房子的事情也基本上落实了,虽然其中经历了种种坎坷,但是结局还是美好的,现在在等贷款放贷.中国人嘛,没有 ...
- xshell 快速复制打开之前用过的ssh
有时候需要对一个ip地址的服务器进行多个操作,不在一个会话中,需要新建多个同样的会话 然后左键单击+号 出现一个新的会话 然后再右键单击+号 选择之前保存过的ip地址即可快速创建!
- Netty实现高性能IOT服务器(Groza)之精尽代码篇中
运行环境: JDK 8+ Maven 3.0+ Redis 技术栈: SpringBoot 2.0+ Redis (Lettuce客户端,RedisTemplate模板方法) Netty 4.1+ M ...
- 第四次作业—— 性能测试(含JMeter实验)
一.回答下述问题 1.性能测试有几种类型?它们之间什么关系? 答:性能测试根据其不同的测试目的分为以下几类. (1)性能验证测试:验证系统是否达到事先已定义的系统性能指标.能否满足系统的性能需求.这种 ...
- javascript模块化编程 从入门到实战
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8& ...
- ASP.NET Core开发者成长路线图
目录 ASP.NET Core开发者路线图RoadMap 免责声明 请给一个星星! ⭐ 路线图 资源 总结 贡献 许可协议 ASP.NET Core开发者路线图RoadMap 来源: MoienTaj ...