为了之后博客的进行,本篇博客我们就来聊一下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. 2~62位任意进制转换(c++)

    进制转换的符号表为[0-9a-zA-Z],共61个字符,最大可表示62进制. 思路是原进制先转换为10进制,再转换到目标进制. 疑问: 对于负数,有小伙伴说可以直接将符号丢弃,按照整数进行进位转换,最 ...

  2. 细说Nullable<T>类型

    目录一.简介二.语法和用法三.类型的转换和运算四.装箱与拆箱五.GetType()方法六.ToString()方法七.System.Nullable帮助类八.语法糖 一.简介 众所周知,值类型变量不能 ...

  3. 用awk写递归

    看到自己很多年前写的一篇帖子,觉得有些意义,转录过来,稍加修改. awk是一种脚本语言,语法接近C语言,我比较喜欢用,gawk甚至可以支持tcp/ip,用起来非常方便. awk也支持递归,只是awk不 ...

  4. 【LeetCode】89. Gray Code

    题目: The gray code is a binary numeral system where two successive values differ in only one bit. Giv ...

  5. ajax数据请求2(json格式)

    ajax数据请求2(json格式) <!DOCTYPE html> <html> <head> <meta charset="UTF-8" ...

  6. Example007关闭窗口时关闭父窗口

    <!--实例007关闭窗口时刷新父窗口--> <!DOCTYPE html> <html lang="en"> <head> < ...

  7. Java泛型概念

    1. 概述在引入范型之前,Java类型分为原始类型.复杂类型,其中复杂类型分为数组和类.引入范型后,一个复杂类型就可以在细分成更多的类型.例如原先的类型List,现在在细分成List<Objec ...

  8. maven(二) maven项目构建ssh工程(父工程与子模块的拆分与聚合)

    前一节我们明白了maven是个什么玩意,这一节就来讲讲他的一个重要的应用场景,也就是通过maven将一个ssh项目分割为不同的几个部分独立开发,很重要,加油 --WH 一.maven父工程与子模块的拆 ...

  9. 在suse上折腾iptables

    需求背景:有台服务器希望屏蔽掉某IP对它的SSH连接. 临时客串下DevOps,下面的做法可能在专业运维的同学里不太专业,还请指教. 该服务器的操作系统是SuSE Linux,服务器上是安装了ipta ...

  10. Unity3D中使用BMFont制作图片字体 (NGUI版)

    [旧博客转移 - 发布于2015年9月10日 16:07] 有时美术会出这种图片格式的文字,NGUI提供了UIFont来支持BMFont导出的图片字体 BMFont原理其实很简单,首先会把文字小图拼成 ...