上篇博客我们对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()方法的特定使用的快捷方式。

  

2、上述扩展的使用方式

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

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

  

二、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的整数值信号量的。

  

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

  

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

  • 首先类型为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中。

  

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

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两个,具体如下所示。

  

2、Filter的代码实现

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

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

  

3、执行原理图

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

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

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

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



ReactiveSwift源码解析(五) SignalProtocol的observe()、Map、Filter延展实现的更多相关文章

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

    上篇博客我们对Signal的基本实现以及Signal的面向协议扩展进行了介绍, 详细内容请移步于<Signal中的静态属性静态方法以及面向协议扩展>.并且聊了Signal的所有的g功能扩展 ...

  2. ReactiveSwift源码解析(六) SignalProtocol的take(first)与collect()延展实现

    上篇博客我们聊了observe().map().filter()延展函数的具体实现方式以及使用方式.我们在之前的博客中已经聊过,Signal的主要功能是位于SignalProtocol的协议延展中的, ...

  3. Celery 源码解析五: 远程控制管理

    今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...

  4. ReactiveSwift源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  5. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  6. ReactiveSwift源码解析(一) Event与Observer代码实现

    ReactiveCocoa这个框架是做什么用的本篇博客就不做过多赘述了,什么是"响应式编程"也不多聊了,自行Google吧.本篇博客的主题是解析ReactiveCocoa框架中的核 ...

  7. ReactiveSwift源码解析(二) Bag容器的代码实现

    今天博客我接着上篇博客的内容来,上篇博客我们详细的看了ReactiveSwift中的Observer已经Event的代码实现.接下来我们来看一下ReactiveSwift中的结构体Bag的实现.Bag ...

  8. Spring 源码解析之DispatcherServlet源码解析(五)

    spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一 ...

  9. iOS即时通讯之CocoaAsyncSocket源码解析五

    接上篇:iOS即时通讯之CocoaAsyncSocket源码解析四         原文 前言: 本文为CocoaAsyncSocket Read篇终,将重点涉及该框架是如何利用缓冲区对数据进行读取. ...

随机推荐

  1. PowerDesigner如何将设计的表更新到数据库中

    前言: 软件开发的过程中,将设计的表更新到数据库中是一件繁琐的事情,使用好工具,能够事半功倍. 环境介绍:Oracle 11g x64 前期准备: 1.PowerDesigner工具(本人是32位的) ...

  2. 解决Kali用户名密码正确但是无法登陆的问题

    本文由荒原之梦原创,原文链接:http://zhaokaifeng.com/?p=684 前言: 用户名和密码都正确,但是登陆Kali的时候总是提示用户名或密码错误,无法登陆,本文介绍一种解决办法. ...

  3. SQL*Loader FAQ

    SQL*Loader FAQ: Contents [hide]  1 What is SQL*Loader and what is it used for? 2 How does one use th ...

  4. 手机端实现fullPage——全屏滚动效果

    封装了一个小插件模拟fullPage的全屏滚动效果,比较简单. 特点: 1.  纯js实现,小巧轻便. 2.  兼容性好.苹果.安卓都没问题,暂时没遇到问题机型. 缺点: 1.  仅封装了基础功能,H ...

  5. vue的常用组件方法应用

    项目技术: webpack + vue + element + axois (vue-resource) + less-loader+ ... vue的操作的方法案例: 1.数组数据还未获取到,做出预 ...

  6. Java 学习笔记 (二) Selenium WebDriver Java 弹出框

    下面这段实例实现了以下功能: 1. profile使用用户本地电脑上的 (selenium 3有问题.因为selenium 3把profile复制到一个temp文件夹里,但并不复制回去.所以每次打开仍 ...

  7. Executor框架简介

    Executor框架是在Java5中引入的,可以通过该框架来控制线程的启动,执行,关闭,简化并发编程.Executor框架把任务提交和执行解耦,要执行任务的人只需要把任务描述清楚提交即可,任务的执行提 ...

  8. 解锁 vmware esxi 6.7 并安装 mac os 10.13

    1.安装 esxi 6.7 2.下载 unlocker 2.1.1.zip 3.上传 unlocker 2.1.1.zip esxi的磁盘中 4.开启esxi的ssh登录 5.使用 ssh 登录 es ...

  9. 【BZOJ 2673】[Wf2011]Chips Challenge

    题目大意: 传送门 $n*n$的棋盘,有一些位置可以放棋子,有一些已经放了棋子,有一些什么都没有,也不能放,要求放置以后满足:第i行和第i列的棋子数相同,同时每行的棋子数占总数比例小于$\frac{A ...

  10. BZOJ_1455_罗马游戏_可并堆

    BZOJ_1455_罗马游戏_可并堆 Description 罗马皇帝很喜欢玩杀人游戏. 他的军队里面有n个人,每个人都是一个独立的团.最近举行了一次平面几何测试,每个人都得到了一个分数. 皇帝很喜欢 ...