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

时间:2023-01-30 05:53:56

上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于《Signal中的静态属性静态方法以及面向协议扩展》。并且聊了Signal的所有的g功能扩展都是放在Signal所实现的SignalProtocol协议的扩展中的。本篇博客就沿袭上篇博客的内容,我们来聊一下SignalProtocol的部分扩展。本篇博客我们主要来聊一下对Signal添加Observer的observe()方法扩展的具体实现,并且聊一下Signal的MapFilter相关的功能扩展的具体实现。

当然我们在聊相关源码的具体实现时,会给出相关的测试用例,然后再根据测试用例来理解其代码实现。

一、observe()方法的扩展

首先我们来看一下observe()方法的扩展。通过前几篇博客的介绍,我们知道SiganlObserver之间的关联是通过observe()方法来实现的。而observe()方法的核心实现在上篇博客中已经进行了详细介绍。而在协议扩展中又对observe()方法进行了一些扩展,这些扩展主要是针对一些特定功能为observe()的使用方式添加快捷调用方式。

1、observe()方法扩展的具体实现

下方个SignalProtocol的延展主要是对observe()方法的扩展,在每个扩展方法中最后还是得调用Signal类中所实现的observe()方法。还是那句话,下方的这些observe()方法的扩展主要还是Signal类中observe()方法的快捷方式。下方将对observe()的每个快捷方法进行介绍。

  • observe(action) : 该方法传入的是一个Action闭包,该Action闭包其实就是Observer类中的Action闭包。也就是在调用observe(action)方法时,为Observer的Action提供了闭包体。而在observe(action)方法中要做的就是实例化Observer对象,并将该对象传给Signal的observe()方法。稍后会给出该扩展方法的使用过程。
  • observeResult(result):该扩展方法是将Observer的value事件和failed事件分别转换成Result枚举的success和failure。
  • observeCompleted(completed): 该扩展方法所接收的闭包就是Observer接收completed事件所执行的闭包。
  • observeFailed(failed): 该扩展方法所接收的闭包就是Observer接收failed事件所执行的闭包。
  • observeInterrupted(interrupted): 该扩展方法所接收的闭包就是Observer接收interrupted事件所执行的闭包。

从下方代码片段中我们不难看出,都是在Signal的observe()方法的基础上做的扩展,本质上就是observe()方法的特定使用的快捷方式。

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

2、上述扩展的使用方式

看完实现,在看上述方法的使用方式就简单多了。下方代码片段就是上述扩展中每个方法的使用方式。我们可以根据具体的业务场景以及具体的功能来选择实现那种方法来满足自己的开发需求。从下方每个方法中的调用方式可以看出,每个方法在调用时所提供的尾随闭包就是该方法所表示的快捷方式。

当然,下方所有的方法,我们都可以使用Signal中的observe()方法来实现,只不过没有下方这些方法方便快捷。

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

二、SignalProtocol的Map扩展

在《ReactiveSwift源码解析之Event与Observer》这篇博客中我们聊了Event的Map函数,主要是将一个类型的Event(如Event<Value, Error>)转换成另一个Event类型(如Event<U, Error>)。Signal的Map函数也不例外,也是将一个类型的Signal转换成另一个类型的Signal。当然,Signal的Map函数本质上还是使用了Event的Map函数。

根据之前对ReactiveSwift框架的解析,我们不难发现Signal、Observer以及Event三者要想进行沟通是其泛型类型必须是相同的,也就是一套的。Map函数就是为了解决Signal类型与Observer的类型不匹配而生的。也就是说我们可以通过Map函数的处理,一个Int类型的Signal可以发送给String类型的Observer的,如果没有Map函数的支持,是做不到这一点的。

可以说Map函数是“适配器模式”的一种应用方式。可以将不同类型的信号量和观察者进行适配使其正常通信。接下来我们就来看一下SignalProtocol协议的Map相关的扩展以及使用方式。

1、map<U>() -> Signal<U, Error> 映射函数

map<U>()就是扩展中的Map相关函数之一。该函数是一个泛型函数,其返还值是一个Signal<U, Error>类型的对象。也就是说,一个Signal<Value, Error>类型的信号量可以通过map<U>()函数映射成一个Signal<U, Error>类型的信号量。当然map<U>()函数的参数是一个尾随闭包,该闭包有map函数的调用者提供,目的就是为了让用户自定义两个信号量之间的映射规则。

首先我们来看一下map函数的使用方式,下方代码片段中是map函数的使用示例以及输出结果,下方是对这段代码的解释:

  • 首先我们通过Signal的pipe()静态方法创建了一个类型为Signal<Int, NoError>的信号量signal。
  • 然后通过signal的map函数创建了一个新的类型为Signal<String, NoError>的信号量mappedSignal。map函数的尾随闭包中就是映射规则,其中value是Int类型,而返回值是String类型。
  • 然后创建了一个Observer<String, NoError>类型的观察者subscriber, 并将subscribermappedSignal进行关联
  • 最后我们调用signalobserver对象发送value事件,该事件所携带的值为整数10。由输出结果我们可以知道,与mappedSignal关联的观察者subscriber尽管只接收String类型的事件,但是经过map函数的处理此刻也是可以收到来自signal的整数值信号量的。

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

看完map函数的用法后我们来看一下其具体的代码实现。下方就是上述示例所调用的map()函数的具体实现代码。在map()函数中返回了一个类型为Signal<U, Error>的信号量对象。在Signal的构造器的尾随闭包中又调用了observe(action)方法将新创建的Signal的observer对象所对应的action添加到了之前Signal对象中。

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

上述代码的执行过程也许有些绕,我们可以通过一张简图来看一下上述代码的执行过程。下方是对该过程进一步的解释:

  • 首先类型为Signal<Value, Error>的signal对象调用其map<U>()函数生成了一个新的类型为Signal<U, Error>的newSignal对象。
  • 然后通过Event的map<U>()函数,将signal对象中类型为Event<Value, Error>的事件通过调用事件的map<U>()函数将其映射成类型为Event<U, Error>的newEvent事件对象。
  • 然后我们将新的newEvent添加到newSignalobserveraction中。
  • 然后使用newSignalobserver的action创建一个类型为Observer<Value, Error>的newObserver对象。
  • 最后将这个新的newObserver对象添加到旧的signalBag中。

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

上述是代码的调用步骤,我们可以看一下具体的执行过程,如下图所示。从下图的结构我们不难看出map()函数是链式发展的,下发的mappedSignal还可以调用其自己的map()函数来生成新的Signal对象。在这个链上的所有Observer都会接受到最原始的Signal对象所发出的事件消息。signal与mappedSignal桥接的最终手段还是Event的map()函数。

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

2、mapError<F>() -> Signal<U, F> 映射函数

mapError<F>()映射函数的实现机制和使用方式与上述的映射函数即为相似。只不过该映射函数使用了Event的mapError<F>()函数。因该映射还是的实现方式与上述函数类似,在此就不做过多赘述了。

关于lazyMap<U>()的实现和使用方式,我们暂且不说,后边聊到SignalProducer以及Flatten时我们再做补充。

三、SignalProtocol的filter扩展

Filter顾明思议,就是用来过滤东西的。如果你理解上述map的工作原理的话,Filter就显得简单多了。Filter的工作原理以及实现方式与map相似,只不过将Event的map改成了过滤条件。首先我们将会给出Filter的使用方式,然后在该处Filter的代码实现方式并给出工作原理图。

1、Filter的使用方式

关于Filter的使用,我们就使用ReactiveSwift官方的示例,下方是对该示例的解释:

  • 首先使用pipe创建一个signal,然后获取到该signal发送消息的句柄observer。
  • 然后通过调用signal的filter()函数来获取过滤信号量filteredSignal,filter()函数的尾随闭包中跟着的是过滤条件。
  • 然后往filteredSignal信号量中添加观察者subscriber。
  • 当使用signal信号量发送事件时,符合过滤条件的事件才会被过滤信号量filteredSignal所关联的观察者接收

下方截图中我们的过滤条件是事件绑定的值必须大于12,也就大于12的Value事件才会被观察者接受,所以输出的结果只有13和14两个,具体如下所示。

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

2、Filter的代码实现

看完Filter的使用方式,接下来我们来看一下Filter的代码实现方式。下方代码片段就是filter函数的具体实现,从代码结构上来看,与上述的map函数差不多,都是返回一个新的Signal对象,新的Signal对象与原来的Signal对象之间有一个桥接观察者来进行通信的。self.observer()函数后边的闭包就是桥接观察者从原信号量中发出的事件,然后在该事件中根据过滤条件来判断是否向新的信号量所绑定的所有观察者转发该事件。

从下方代码中我们明确的可以看出,当条件闭包predicate()的值为true时,observer就会对值的事件进行转发,然后过滤信号量所绑定的观察者就可以收到这些事件了。

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

3、执行原理图

下方就是filter的执行原理图,该图的结构与map()函数的执行结构类似。从下方图中我们不难看出,filter()函数也是支持链式发展的,就是可以在新的filterSignal的对象上我们任然可以添加新的过滤条件条件。因为无论是map()还是filter()函数都会返回一个新的Signal对象,并且两者都是可以链式发展的,所以我们可以这样去写signal.map().filter().map().filter().filter()……

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

在filter下方还有一个filterMap<U>()函数,该函数的主要功能是用来Map的,代码实现方式与与上述的filter类似,只不过是map的功能,但是该map功能有过滤功能,可以过滤掉nil的值。扩展中的skipNil()方法中调用的就是filterMap<U>()函数,在此就不做过多赘述了。

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

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

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #ffffff }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #08fa95 }
span.s1 { }
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #ffffff }
span.s1 { }