为了之后博客的进行,本篇博客我们就来聊一下ReactiveSwift框架中的Lifetime类的具体实现。从Lifetime这个名字中我们就这道,就是生命周期。在ReactiveSwift中使用Lifetime来标记一个对象的生命周期,其实主要功能还是将对象的deinit()析构函数通过发送信号量将其回调出来。接下来我们就来看一下Lifetime类的实现。Lifetime类与Event和Observer相似,也是比较原子性的类,以原子组件的形式存在于ReactiveSwift中。

下方我们会先给出一个Lifetime的使用示例,然后根据示例的输出结果来看一下Lifetime的具体代码实现,以及工作原理。

一、Lifetime使用实例

针对Lifetime的特性,我们给出了下方的示例。当然ReactiveSwift官网上是没有关于Lifetime的单独示例的,因为Lifetime不单独的对外服务。下方就是我们对Lifetime类而写的示例。

1、lifetime()方法实现

首先我们来看一下下方的lifetime()方法。。首先通过Lifetime类中的make()工厂方法创建了一个Lifetime的对象和该对象对应的token。并且lifetime()方法在调用时,需要一个引用参数tokenRef,也就是说tokenRef是inout类型的参数,通过tokenRef参数可以把make()所返回的token传到函数外部。

然后使用lifetime对象的observeEnded()方法来添加两个观察者。后边紧跟着的尾随闭包是token被释放时所执行的闭包块。因为lifetime对象除了在lifetime()方法中使用到,再也没有其他地方的引用了,根据ARC中Strong类型的特点,所以在lifetime()方法调用结束后lifetime对象就会被释放掉。所以我们在lifetime()函数的结尾处给出了“lifetime将要被释放”的Log。

而lifetime()方法中的token对象通过tokenRef这个inout类型的参数被方法之外的作用域使用到,所以在lifetime()方法调用结束后token所对应的堆空间并不会被释放。

 

2、lifetime()方法的调用

在tapLifetimeButton()方法中,我们对上述的lifetime()方法进行了调用。首先我们声明了一个类型为Any?的tokenRef变量,该变量在调用lifetime()函数时作为参数传给lifetime()方法。也就是说tokenRef变量引用了lifetime()方法中的token对象所对应的堆空间。

当lifetime()被调用后,因为lifetime()中的lifetime对象所对应的堆空间只用在lifetime()的作用域中被引用到,所以当该方法执行完毕后,lifetime所对应的堆空间会立即被释放掉。

当tokenRef被置为nil后,token所对应的堆空间也会被立即释放掉。在token被释放执行token对应的析构函数时,会给那些通过lifetime的observeEnd()方法添加的观察者发送被析构的消息。所以token被释放时,会执行observeEnd()方法的尾随闭包。

  

3、运行结果

下方截图就是上述示例的运行结果,我们可以根据下方的输出结果与上述的代码实现进行对比。从输出结果中我们容易知道,lifetime对象是在lifetime()方法调用执行后所释放的。而在lifetime()方法中所分配的token对象所对应的堆空间是在tokenRef被置为nil时所释放的,在释放之前像观察lifetime的生命周期的观察者来发送消息。

从运行结果来看,Lifetime这个生命周期的类,本质上是通过token来标示一个对象的生命周期的,和Lifetime对象的释放是没有什么直接关系。稍后,我们聊Token类以及Lifetime类时,会一目了然。

  

二、Lifetime中的内部类Token

看完Lifetime的使用示例,我们来看一下Lifetime的内部代码实现。在Lifetime类的内部定义个了一个Token类。Token类的对象会在Lifetime中使用到,稍后会给出Token的使用方式。接下来我们就先来看看这个Lifetime类内部的Token类的代码实现。

Token类的实现比较简单,一句话概括Token的功能:其中使用了Signal的pipe方法创建了一个ended信号量,并获取到了ended信号量发送事件的endedObserver,然后在deinit析构函数中使用endedObserver的sendCompleted()的方法发送Complete事件。上这句话就概括了Token中的全部功能。

下方就是Token类的代码实现,其中有一点需要我们注意的是在Token的ended信号量所发送的Value值的类型是一个无参闭包。在之后的内容中,用到的时候在介绍。

  

三、Lifetime的ended属性和构造器

聊完Token的代码实现,我们就来聊一下Lifetime中的对象属性以及构造器。在Lifetime类中只有一个对象属性,那就是ended信号量。该信号量的类型也是一个可以发送无参无返回值的闭包的Value。Lifetime的构造器主要就是给ended赋值。具体代码如下所示。

  

四、Lifetime的便利构造器、工厂方法以及empty静态属性

1、Lifetime的便利构造器

接下来我们就来看一下Lifetime的便利构造器。下方代码片段中被convenience关键字修饰的就是Lifetime的便利构造器。该便利构造器的参数是一个Token类型的对象,而在便利构造器中调用了Lifetime的构造器,将Token对象的ended信号量传给了Lifetime的构造器。所以Lifetime中的ended信号量其实就是Token对象中的ended信号量。

2、Lifetime的工厂方法

聊完Lifetime的便利构造器后,我们就来聊一下Lifetime的工厂方法。我们在之前的博客中《设计模式(四):从“兵工厂”中探索简单工厂、工厂方法和抽象工厂模式》详细的介绍了工厂模式。而下方代码片段中的make()静态方法在Lifetime类中所扮演的角色就是工厂方法,负责创建Lifetime类的对象。在make()方法中主要做了两件事情,一个是实例化了一个Token对象token,然后将token传给Lifetime的便利构造器。最后将token以及Lifetime的便利构造器所创建的Lifetime类的对象以元组的形式进行返回。具体代码如下所示。

3、Lifetime的empty静态计算属性

Lifetime的empty静态计算属性类似于Signal的empty静态计算属性,从其实现代码我们可以看出empty所创建的Lifetime对象中的ended信号量是一个Signal.empty类型的信号量。也就是说empty所创建的Lifetime对象是一个已经结束的生命周期。具体代码如下。

  

五、observeEnded()方法实现

接下来我们来看一下observeEnded()方法的代码实现。该方法的主要目的就是往ended信号量中的Bag容器中添加观察者的。当观察者收到的事件是isTerminating时,会执行observeEnded()方法所提供的尾随闭包。

而这个endend信号量本质上就是token对象中的endend信号量。当token对象被析构时,会在token的析构函数中调用endend信号量发送信号的endendObserver的sendComplete()方法来发送complete事件,也就是上述代码在Token类的deinit方法中所作的事情。

  

六、Lifetime执行原理图

下方是Lifetime、Token、Signal、Observer间的执行关系图。在Lifetime类中,token起着至关重要的作用。Lifetime的工作原理中其实是使用Token的对象的生命周期来表示一个对象的生命周期的。如果Lifetime的对象空间被释放了,但是Token对象所对应的堆空间任然存在,那么Lifetime所对应的Object的生命周期任然再延续,这一点从上面的代码示例中可以明确看出。

还是那句话,本质上是使用Token对象的生命周期来表示一个对象的Lifetime。

  

今天的博客就先到这儿,下篇博客我们会继续解析ReactiveSwift框架中的其他内容。

上述代码github分享地址:https://github.com/lizelu/TipSwiftForRac

ReactiveSwift源码解析(十) Lifetime代码实现的更多相关文章

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

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

  2. ReactiveSwift源码解析(十二) MutableProperty基本代码实现

    前两篇博客我们分别聊了ReactiveSwift框架中的负责标记对象的生命周期的类Lifetime以及负责原子性操作的Atomic类的具体代码实现.前两篇博客之所以聊Lifetime以及Atomic的 ...

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

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

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

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

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

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

  6. ReactiveSwift源码解析(二) Bag容器的代码实现

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

  7. vue系列---Mustache.js模板引擎介绍及源码解析(十)

    mustache.js(3.0.0版本) 是一个javascript前端模板引擎.官方文档(https://github.com/janl/mustache.js) 根据官方介绍:Mustache可以 ...

  8. Spring源码解析 - springMVC核心代码

    一.首先来讲解下springMVC的底层工作流程 1.首先我们重点放在前端控制器(DispatcherServlet) 其类图: 因为从流程图看,用户的请求最先到达就是DispatcherServle ...

  9. Vue源码解析-调试环境-代码目录和运行构建

    目录 前言 1 代码结构 1.1 octotree插件 1.2 vue工程项目目录 1.3 主要代码目录src compiler core platforms server sfc shared 2 ...

随机推荐

  1. kaggle Titanic心得

    Titanic是kaggle上一个练手的比赛,kaggle平台提供一部分人的特征,以及是否遇难,目的是预测另一部分人是否遇难.目前抽工作之余,断断续续弄了点,成绩为0.79426.在这个比赛过程中,接 ...

  2. 探索Windows命令行系列(2):命令行工具入门

    1.理论基础 1.1.命令行的前世今生 1.2.命令执行规则 1.3.使用命令历史 2.使用入门 2.1.启动和关闭命令行 2.2.执行简单的命令 2.3.命令行执行程序使用技巧 3.总结 1.理论基 ...

  3. oracle sql语句跟踪及性能分析工具实现

    在网上找了一大圈,没找着合适的工具来跟踪oracle一段时间的sql. 我们的场景是打算自动化跑遍所有场景(rft)+fiddler跟踪请求+后端跟踪sql,根据结果去分析慢的请求和sql,本来awr ...

  4. js验证input是否输入数字

    onkeyup="if(isNaN(value))execCommand('undo')" onafterpaste="if(isNaN(value))execComma ...

  5. 探索Windows命令行系列(6):活用批处理解决实际问题

    1.批量修改文件名 2.批量重启服务 3.全盘搜索指定文件 3.1.全盘搜索名称为 mm.jpg 的文件,获取其全路径 3.2.查找系统中所有名称以 .docx 结尾的文件 4.调用可执行程序 4.1 ...

  6. 学编程担心自己英语不好吗?(IT软件开发常用英语词汇)

    发一份,我们导师的收集的常用词汇,与大家共享 欢迎加入Java学习交流裙六一六九五九四四四! S 欢迎加入Java学习交流裙 六一六  九五九  四四四!

  7. Ubantu 16.4 samba安装配置

    本文总结了Ubantu 16.04 环境下的samba安装.配置及使用.本文为原创,也是我的第一篇博客,以后会经常写博客,记录自己的学习.总结及研究,让博客见证着我成长的轨迹. 下文中的所有命令均使用 ...

  8. MyEclipse2014web工程项目直接复制不能访问报错处理方案

    在学习web servlet项目中,做一个项目时 , 完成到了某一阶段 实现了部分功能,有必要保存这一项目,当复制这个项目之后发现发布会报错或者不能访问. 其实复制项目主要是为了在以后的学习中如果能顺 ...

  9. Java IO学习笔记(五)对象流

    1.Object流:直接将Object写入或读出. 2.序列化:将Object直接转化成字节流写到硬盘或网络上. 3.如果想把一个对象转化成字节流,该对象的实体类必须得实现Serializable接口 ...

  10. unslider插件的使用

    深入理解unslider.js源码 最近用到了一个挺好用的幻灯片插件,叫做unslider.js,就想看看怎么实现幻灯片功能,就看看源码,顺便自己也学习学习.看完之后收获很多,这里和大家分享一下. u ...