ReactiveSwift源码解析(十) Lifetime代码实现

为了之后博客的进行,本篇博客我们就来聊一下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()方法的尾随闭包。

  ReactiveSwift源码解析(十) Lifetime代码实现

3、运行结果

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

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

  ReactiveSwift源码解析(十) 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值的类型是一个无参闭包。在之后的内容中,用到的时候在介绍。

  ReactiveSwift源码解析(十) Lifetime代码实现

三、Lifetime的ended属性和构造器

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

  ReactiveSwift源码解析(十) Lifetime代码实现

四、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对象是一个已经结束的生命周期。具体代码如下。

  ReactiveSwift源码解析(十) Lifetime代码实现

五、observeEnded()方法实现

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

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

  ReactiveSwift源码解析(十) Lifetime代码实现

六、Lifetime执行原理图

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

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

  ReactiveSwift源码解析(十) Lifetime代码实现

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

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

上一篇:[LeetCode]Delete and Earn题解(动态规划)


下一篇:LC 740. Delete and Earn