为了之后博客的进行,本篇博客我们就来聊一下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. C#开发移动应用系列(3.使用照相机扫描二维码+各种基础知识)

    前言 上篇文章地址: C#开发移动应用系列(1.环境搭建) C#开发移动应用系列(2.使用WebView搭建WebApp应用) 今天我们来讲一下如何使用Camera来调用照相机扫描二维码. (Tips ...

  2. 小程序解析html标签wxPrase插件

    微信小程序的标签和原来我们习惯用的标签是不一样的,例如视图容器标签小程序是view,然而html就很多比如常用的div就和小程序的view类似. 通常我们在开发小程序(从列表页跳转到详情页)通过富文本 ...

  3. 自己动手写一个自动登录脚本gg

    1.下载一个sshpass工具 2.安装sshpass,安装到tools文件夹 3.把tools文件夹的路径加入到/etc/bashrc vim   /etc/bashrc 最后一行  : expor ...

  4. PHP加密解密数字

    <?php /** * 加密解密类,PHP加密解密数字,适用于URL加密 * 该算法仅支持加密数字.比较适用于数据库中id字段的加密解密,以及根据数字显示url的加密. * @version a ...

  5. xUtils使用详细介绍

    xUtils3使用详解 一.xUtils简介: xUtils是基于Afinal开发的目前功能比较完善的一个Android开源框架,官网:https://github.com/wyouflf/xUtil ...

  6. 怎样把echarts图表做成响应式的

    如果想要把echarts图表给做成响应式的那么就应该用rem 单位,给图表的外围容器设置rem 单位,然后调用jquery 的resize方法,$(window).resize(function(){ ...

  7. Java编程练习(四)——集合框架应用

    Java集合框架小应用之扑克牌小游戏 学习了Java集合框架之后,我写了一个扑克牌小游戏来巩固知识.学习之余的练习之作,有不足之处还得多多指教了~(*/ω\*) 扑克牌小游戏背景: 1. 创建一副扑克 ...

  8. Mysql连接出错问题

    1.java 提示:java.lang.ClassNotFoundException: com.mysql.jdbc.Driver 处理:导入mysql-connector-java-5.1.7-bi ...

  9. spring注解@service("service")括号中的service有什么用

    相当于 xml配置中得 bean id = service 也可以不指定 不指定相当于 bean id = com. service.service 就是这个类的全限定名 好处是:同一个接口可以有多个 ...

  10. 无法启动 IIS Express Web 服务器

    问题描述:我用的是vs2015,有时候打开自己的项目,点击调试运行,会失败,弹出窗口,告诉我,无法启动 IIS Express Web 服务器,我就纳闷了,刚才还好好,怎么这会就不行了,各种试,都不行 ...