ReactiveSwift源码解析(九) SignalProducerProtocol延展中的Start、Lift系列方法的代码实现

上篇博客咱们聊完SignalProducer结构体的基本实现后,咱们接下来就聊一下SignalProducerProtocol延展中的start和lift系列方法。SignalProducer结构体的方法扩展与Signal的扩展相同,都是面向协议的扩展。首先建立了一个SignalProducerProtocol协议,使SignalProducer在延展中遵循SignalProducerProtocol协议。而后咱们再对SignalProducerProtocol进行扩展。这样一来,SignalProducer结构体就拥有了咱们在SignalProducerProtocol协议中扩展的方法了。这也是咱们以前所说的“面向协议的扩展”。git

今天咱们就来聊一下SignalProducerProtocol协议扩展中的start和lift系列方法。不管是start系列方法仍是lift系列方法,都是在SignalProducerstartWithSignal(setup)核心方法的基础上构建的。而关于startWithSignal(setup)方法的具体实现,上篇博客给出了相应的介绍并给出了该核心方法的使用方式,在此就不作过多赘述了。github

而在SignalProducerProtocol协议扩展中的方法,基本上全是对b方法的封装,只不过使用场景和功能更为专注,用法更为方便。接下来咱们就来看一下SignalProducerProtocol协议扩展的start和lift系列方法。算法

 

1、Start系列方法闭包

SignalProducerProtocol协议扩展中的Start系列方法的主要做用是往SignalProducer中的signal的Bag中添加观察者的,这一点与Signal的observer()系列方法相似。下方就是start系列的部分方法,在下列的方法中,核心的是start(observer)方法。该方法的参数是一个观察者的对象,start(observer)方法就负责将该观察者添加到SignalProducer中的signal的Bag。而下方一系列的start方法都是调用的start(observer)方法。框架

由于Start系列方法极为类似,在此就不一一进行列举了,下方是部分start方法,其他省略的与下方代码的实现原理一致,在此就不作过多赘述了。函数

  

 

看完start系列方法的实现,咱们再来看一下start系列方法使用方式。下方是start系列方法的部分使用案例,具体介绍以下:测试

  • 首先经过 SignalProducer的init(value)构造器建立一个producer对象,备用。
  • 而后再建立一个观察者 subscriber1,并给出Value事件的处理闭包。
  • 而后调用 start(observer)和startWithSignal()方法将subscriber1添加到信号量中。
  • 以后再调用一些列start()方法往signal中添加新的信号量。

下方的控制台中是该代码段实例的输出结果,从输出结果咱们咱们知道,start()系列方法的主要做用是往signal中添加观察者的。观察者添加完毕后,就调用SignalProducer构造函数的尾随闭包。具体代码以下所示。spa

  

 

 

2、闭包类型的高级用法3d

在聊Lift以前呢,咱们先来看一下闭包类型使用的示例。由于Lift相关方法的实现较复杂一些,其中涉及闭包方法类型的一些高级用法。接下来咱们从Swift语言的角度来看一下函数类型的高级用法,该用法也是在Lift中使用到的,仍是有必要单独的拎出来聊一下的。下方是一些具体的示例。orm

一、建立MyClass类

首先咱们建立一个简单的类MyClass,该类比较简单,就是一个属性、一个构造器、一个add方法。add(other)方法是MyClass的主角稍后咱们会用到。由于下方的代码比较简单在此就不作过多赘述了。MyClass下方紧跟着的就是MyClass类测试用例,以下所示。

  

 

二、建立MyClassProducer类

接下来咱们建立一个MyClassProducer类,在该类中使用到了MyClass类。在MyClassProducer中也有一个add方法,只不过该add方法接收的是一个闭包参数,而返回值是一个MyClass类型。add()的参数类型为(MyClass) -> (MyClass) -> MyClass。该闭包类型接收一个MyClass类型的参数,而后返回一个(MyClass) -> MyClass类型的闭包。而(MyClass) -> MyClass类型的闭包的参数又是一个MyClass类型,而返回值是MyClass。

在add(closure)方法中直接执行了closure闭包,并将闭包最终返回的MyClass类型的对象进行返回。具体代码以下所示。

  

 

三、MyClassProducer类的很是规用法

接下来咱们就来看一下MyClassProducer类中add(closure)方法直接的使用方式。固然,在正常状况下,上述写法尤其繁琐,而是使用方式也是比较麻烦的。下方咱们就来看一下直接调用add(closure)方法的代码。

  • 首先咱们建立了一个MyClassProducer类型的对象myProducer1。
  • 而后直接调用myProducer1的add(closure)方法。add()的尾随闭包的参数是MyClass类型的对象myclass1,其返回值是(MyClass)->MyClass类型的闭包,因此咱们就直接在尾随闭包块中返回了一个闭包块,该返回的闭包块的类型就是 (MyClass)->MyClass。而后在(MyClass)->MyClass类型的闭包块中返回了一个MyClass类型的对象,在建立该对象时,使用到了上述两个闭包的参数myclass1和myclass2。
  • 而后咱们将sum01对象的des属性进行打印,就是closure闭包两个参数的和。具体结果以下所示。

  

 

四、MyClassProducer类中add方法的常规用法

上一部分中add(closure)方法的使用方式是很是规用法,由于直接使用add(closure)方法显得晦涩难懂,并且闭包嵌套闭包,闭包返回闭包的形式着实让人费解。能够说直接使用没有什么好处。接下来咱们就来看一下add(closure)的常规使用方式。

下方代码片断是add(closure)反复的常规使用方式。从下方代码片断中咱们能够直接看出,add(closure)接收的不在是一个闭包,而是MyClass.add(other:)的类型。其实就是把MyClass.add(other:)这个类型所对应的方法体传给了add(closure)闭包。也就是说add(other:)方法的类型与(MyClass) -> (MyClass) -> MyClass闭包类型是等价的。因此能够将add(other:)方法的方法体提供给add(closure)做为参数。换句话说(MyClass) -> (MyClass) -> MyClass类型等价于MyClass.add(MyClass)->MyClass

这样作的好处就是可让数据与算法进行分离,add(closure)参数闭包对应什么样的算法那么add(closure)就执行什么样的算法。这一点在SignalProducer类中的Lift系列方法中表现的淋漓尽致。稍后咱们会介绍到。

  

 

 

3、Lift系列的核心方法实现

接下来咱们就来看一下SignalProducer中的Lift系列方法的代码实现。固然,由于Lift系列方法比较多,下方会给出Lift系列方法中比较核心的内容,而剩下的未讲解的则是从这些核心方法中延伸出来的方法。接下来咱们就由易到难,来看一下Lift系列方法的代码实现。

一、lift<U, F>(transform)代码实现

该方法算Lift系列中比较独立并且比较核心的方法了。下方代码片断就是该方法的实现。解释以下:

  • 该方法是一个泛型方法,能够容纳两个泛型 <U, F>。其方法参数是一个逃逸闭包transform,而这个闭包的参数是一个类型为 Signal<Value, Error>的信号量,而返回值是类型 Signal<U, F>的信号量,也就是说transform闭包的功能负责将 Signal<Value, Error>类型的信号量通过某些算法转换成Signal<U, F>类型的信号量。而整个函数的返回值是一个 SignalProducer<U, F>类型的信号量生产者。
  • 方法体中,返回了一个新的SignalProducer对象,在SignalProducer构造器的尾随闭包中调用了原SignalProducer对象的 startWithSignal()方法。在 startWithSignal()方法的尾随闭包中将原SignalProducer对象的信号量signal通过transform闭包转换成一个新的信号量后,将新SignalProducer对象的observer添加到这个转换后的信号量的Bag中,成为其观察者。具体代码以下所示。

  

 

为了更进一步来了解上述代码的实现方式以及运行方式,咱们还需结合示例进行分析。下方代码片断就是上述方法的使用示例,介绍以下:

  • 首先建立了一个类型为 SignalProducer<Int, NoError>的对象producer。在该对象的尾随闭包中,发送了一个 Value事件,该事件的值为整数8888。
  • 而后经过producer对象的lift方法建立了一个新的对象liftProducer。在lift方法的尾随闭包中将producer对象内部的 Signal<Int, NoError>类型的signal信号量经过信号量的map方法将其转换成 Signal<String, NoError>类型的信号量,并返回。有下方代码以及lift()方法的实现容易知道,由于liftProducer对象中的Observer对象被添加到转换后的 Signal<String, NoError>类型的信号量中做为观察者,因此liftProducer对象的类型是 SignalProducer<String, NoError>
  • 下方代码中 lift()方法的尾随闭包就是上述函数实现中的transform的闭包体。下方的signal参数就是transform在调用时传入的参数。
  • 接着,咱们有建立了三个类型为 Observer<String, NoError>类型的观察者,而后将这些观察者都添加进行liftProducer对象的信号量中。而后咱们会看到控制台上打印的观察消息。

  

 

根据lift(transform)的代码实现以及上述示例的运行结果,咱们给出了下方的原理图。下方这个简图就是上述示例执行的整个过程,一图胜千言。根据下图结合上述示例应该是一目了然的。在此就不在过多赘述了。

在SignalProducer的延展中,下方的方法全是在上述lift(transform)的基础上实现起来的,归根结底使用的仍是Signal中相应的方法。下方这些方法的工做方式以及运行原理和上面这个图很是类似。只不过是生成中间的信号量的方式不一样。

下方代码片断中每一个方法在使用lift(transform)方法时使用了尾随闭包的简写形式。其中的$0参数就是尾随闭包的Signal参数,$0信号量经过调用其对应的方法生成的新的信号量就是该尾随闭包的返回值。具体以下所示。固然下方只是部分使用lift(transform)的方法,其余的与下方相似,就不作过多赘述了。

  

 

 

二、liftRight<U, F, V, G>(transform)代码实现

在看liftRight方法的代码实现以前呢,仍是须要回顾一下本篇博客的第二部分闭包类型高级用法的内容的。由于本篇博客第二部分中的内容以及使用示例有助于理解liftRight方法的使用方式以及运行模式。

下方代码片断就是liftRight方法的具体实现,咱们须要注意的是liftRight方法是private类型的,也就是说该方法不对用户直接暴漏,用户不能够直接调用该方法。固然,此刻咱们须要看liftRight方法的代码实现,要给出相应的使用示例,因此咱们能够将private改为public。

从下方代码实现中,咱们能够直观的感觉到liftRight方法的代码实现是比较复杂的。其复杂就复杂在liftRight的入参和返回值都是比较复杂的。首先咱们来看一下liftRight参数。其参数是一个名为transform的闭包,该闭包的类型为(Signal<Value, Error>) -> (Signal<U, F>) -> Signal<V, G>,该闭包类型须要一个Signal<Value, Error>类型的参数,其返回值是一个类型的(Signal<U, F>) -> Signal<V, G>闭包。

从该闭包类型,而后在参考第二部分中的(MyClass)->(MyClass)->MyClass闭包类型,以及该闭包类型与MyClass.add(MyClass)->MyClass方法的对应关系。咱们不难看出(Signal<Value, Error>) -> (Signal<U, F>) -> Signal<V, G>类型的闭包等价于Signal<Value, Error>.method(Signal<U, F>)->Signal<V, G>类型的方法。而在Signal类中有好多符合Signal<Value, Error>.method(Signal<U, F>)->Signal<V, G>类型的方法,如Signal中的combineLatest、withLatest、take(until:)、skip(until:)等方法,也就是说这些方法的方法体均可以做为transform闭包的闭包体,稍后咱们会进行介绍。

  

 

三、liftRight<U, F, V, G>(transform)直接调用

按照老规矩,咱们先给出liftRight方法的使用方式,固然此处是liftRight方法的使用方式是很是规的作法,由于咱们是直接拿过来用的。不过这样作更有利于咱们理解liftRight的代码结构和运行方式。

下方代码片断就是咱们直接调用liftRight方法的示例,介绍以下:

  • 首先咱们经过常规方法建立了一个类型为 SignalProducer<String, NoError>的对象producer。
  • 而后根据liftRight方法的代码实现,建立了两个类型别名。 LiftRightProducerClosureType类型就是producer对象调用liftRight方法是所返回的闭包类型,ClosureReturnType则是liftRight方法的参数的闭包所返回的闭包类型。
  • 类型定义好后,就该让producer对象调用liftRight方法了。而下方的 liftRightProducerClosure(SignalProducer)则是该方法返回的闭包常量。在该liftRight的返回闭包中,咱们将producer对象所对应的信号量signal以及 liftRightProducerClosure闭包所接收的SignalProducer对象中的信号量otherSignal,调用了combineLatest方法进行了合并,具体作法以下。
  • 而后又建立了一个strProducer对象,并为其绑定了一个signal信号量。而后执行 liftRightProducerClosure(strProducer),该闭包会返回一个新的otherProducer对象,紧接着执行otherProducer的startWithValues()方法。而后调用strProducer所绑定信号量的Observer发送值。具体结果以下所示:

 

针对上述代码的执行过程,仍是来张图来的直接。下方这张简图就是上述代码的执行过程。执行过程,与上述代码的执行步骤是一一对应的。能够根据代码的运行步骤后下方的简图进行比较。关于下方简图,就不作过多赘述了。

 

四、liftRight<U, F, V, G>(transform)常规使用方式

上面一小节咱们直接调用了liftRight方法,接下来咱们就来看一下liftRight的常规使用方式。所谓的常规使用方式是使用已经实现过的方法的方法体做为transform的闭包体。上面列举了一些Signal中的方法类型与transform的闭包类型等价的方法其中就有Signal.combineLatest(Signal)->Signal方法。接下来咱们就使用combineLatest的方法体来替换上述liftRight方法的尾随闭包。

下方红框中就是咱们替换的内容,其余代码不变。咱们发现替换后,输出结果与咱们以前一致。下方的这种使用方式才是liftRight方法正确的使用姿式

 

看完上述liftRight的实现以及使用方式,接下来咱们就来看一下SignalProducer内部是如何使用liftRight方法的。下方随便找了一个liftRight的使用方式,触类旁通。

 

 

4、liftRight方法与liftLeft方法对比

而在Lift系列方法中,使用liftRight()方法的方式就是上述代码段的方式。稍后咱们会一一介绍。上述这种技巧使用起来仍是比较方便的。出来liftRight方法,还有一个liftLeft()方法。liftLeft()方法的实现方式与liftRight()方法代码实现即为类似,只是producer的startWithSignal()方法的调用顺序不一样。接下来咱们就来看看这二者的不一样之处。

下方代码片断就是liftRight以及liftLeft方法的代码实现。通过对比咱们不难发现二者的主要区别是otherProducer和self的startWithSignal()方法的执行顺序不一样。在liftRight()方法中otherProducer的startWithSignal会先执行完毕,而self的startWithSignal()会后执行完毕。而liftLeft刚好于此相反。

咱们以producer.liftRight()(otherProducer)为例,这个Right指右边的otherProducer的startWithSignal()方法率先执行完毕。而producer.liftLeft()(otherProducer)则指左边的producer的startWithSignal()方法率先执行完毕。

  

 

为了更直观的感觉上述两个方法的不一样之处,特此给出了下方的示例。根据下方示例的输出结果,liftRight与liftLeft的区别一目了然。对下方示例的介绍以下:

  • 首先咱们建立了两个 SignalProducer的对象,一个发送0,1,2的值,另外一个发送A、B、C的值。
  • 而后让producer1对象调用liftRight方法, 使用liftRightProducerClosure来暂存返回的闭包,将producer2传入闭包中。而后调用rightProducer的startWithSignal方法。以相似的步骤调用lifeLeft方法

  

根据上述代码片断的输出结果,咱们不难看出在producer.liftRight()(otherProducer)右边的otherProducer的startWithSignal()方法率先执行完毕。而producer.liftLeft()(otherProducer)则指左边的producer的startWithSignal()方法率先执行完毕。

下方是liftLeft()方法是使用方式,与liftRight用法是一致的,以下所示:

在SignalProducer的好多方法中都是在lift、liftRight或者liftLeft方法的基础上实现的特定功能。之后的博客会陆陆续续的介绍到。因篇幅有限,今天的博客就先到这儿,下篇博客咱们会继续解析ReactiveSwift框架中的其余内容。

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

相关文章
相关标签/搜索