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

ReactiveCocoa这个框架是做什么用的本篇博客就不做过多赘述了,什么是“响应式编程”也不多聊了,自行Google吧。本篇博客的主题是解析ReactiveCocoa框架中的核心模块ReactiveSwift中的两个核心类的实现,也就是对Event和Observer这两个类进行解析。之所以把这两个类放在一块聊,是因为这两个类比较独立,可以说是ReactiveSwift中的两个原子类。Event确切的说是一个枚举,其中有几种事件,而Observer类的对象就是这些事件的发送者。所以把这两个类放在一块是比较合适的。

当然确切的说,本篇博客是对 ReactiveSwift框架 的部分解析,而ReactiveCocoa这个框架又是在ReactiveSwift框架的基础上搭建起来的,所以我们先来看一下ReactiveSwift这个框架中的代码实现。当然,我们之前发表过ReactiveCocoa的相关博文,如《iOS开发之ReactiveCocoa下的MVVM》,该篇博客的主题还是ReactiveCocoa框架的应用,而本篇博客或者说ReactiveCocoa源码解析系列博客是对ReactiveCocoa框架实现的深度解析。当然这种深度解析有一部分是Swift语言层面的,因为ReactiveCocoa框架中有好多Swift语言的高级用法,当然还有一些架构层面的,通过源码实现,我们要分析出这样设计的好处以及优点。

抛去“响应式编程”的概念,ReactiveCocoa的本质还是对“观察者模式”的使用,关于观察者模式,请参考之前的博客《设计模式(二):自己动手使用“观察者模式”实现通知机制》。也可以说ReactiveCocoa是“观察者模式”应用中比较牛X的一个框架。当然,框架在编码实现时还用到了其他设计模式,在解析到相关内容时,我们在对其进行概述。

当然,本篇博客是对ReactiveSwift源码的解析,也就是说你可以在你的工程中仅仅的引入 ReactiveSwift框架 ,GitHub地址为:https://github.com/ReactiveCocoa/ReactiveSwift.git,至于如何将ReactiveSwift引入到的你的工程中,请参考ReactiveSwift下方的README, 当然,本篇博客是使用的Cocoapods来实现的版本管理,当然ReactiveSwift也支持Carthage, 如果你是Mac开发的话,还可以使用Swift自带的包管理器。Swift的包管理器我们在之前聊Swift开发服务端的时候使用到了,不过目前iOS开发中还不能使用Swift自带的包管理器。相信在不久的将来Swift的包管理器将会支持iOS开发的。闲淡适中,开始我们的主题。

本篇博客我们将先在Swift语言的层面来聊一些东西,因为在Event和Observer实现时会用到。然后我们再解析一下Event和Observe的实现。之前我们聊过Swift语法层面的东西,不过今天还是要在聊一下的,结合着实例还聊语法最为实用。

一、Swift中的泛型

在ReactiveSwift以及ReactiveCocoa中大量的用到了泛型以及关联类型,所以在聊源码之前,我们还是有必要回顾一下Swift中的泛型的使用的。当然,只是简单的回顾一下,不是今天博客的重点。首先我们得通过一个实例来看一下泛型的使用。

下方这个代码段,就是在协议中使用 associatedtype 关键字声明了一个关联类型,当然这个关联类型就相当于协议中的泛型了。下方的这个 GenericityClass 类后边的<>中声明的就是该类中使用的泛型类型,我们将该泛型命名为 MyCustomType, 当然我们要求该类型必须是遵循 Comparable 协议的类型,所以声明该泛型的形式为 <MyCustome: Comparable>。声明完该泛型后,在类中我们就可以想使用普通类型那样来使用该泛型了。

泛型不仅仅可以在类中使用,也可以在方法中使用,下方的genericityFunc()方法中就使用了泛型,用法就是在方法名的后方紧跟着泛型,如下所示。

  

接下来我们来看一下上述泛型类的使用方式。下方代码首先声明了一个泛型类的实例,在实例化时,给泛型指定了确定的类型 String。我们还可以为相应的的泛型类型使用 typealias 指定别名,然后使用别名来实例化,如下所示。因为代码比较简单,下方测试用例的输出结果就不往上粘贴了。

  

二、Swift中的枚举

因为今天我们要聊的Event就是个枚举,所以我们先来回顾一下Swift中枚举的使用。当然还是依托于实例。下方代码中的枚举是在我们之前聊Swift的枚举的主题中拿过来的,并且做了相应的修改。当然在Swift中枚举以及结构体都是可以使用泛型的,接下来我们就来好好看一下Swift中强大而灵活的枚举类型。

下方代码片段中我们定义了一个MobileLanguage枚举类型,其中有两个枚举项。一个是iOS,另一个是Android。枚举项iOS的枚举关联值是一个含有两个字符串元素的元组,而Android枚举项的关联值是一个字符串。下方的iOSValueandroidValue是两个计算属性,用来返回相关枚举项的关联值。

当然,我们使用 if-case-let语句来获取相关的枚举关联值,具体如下所示。

  

当然,我们还可以对 “==”运算符进行重载,让其支持上述定义的枚举类型的比较。下方主要还是Switch的使用,当然,之前我们也针对过Switch单独进行过讲解,下方就是Switch对元组的匹配,并且在相应的case中获取枚举的关联值,如下所示。

  

下方就是上述枚举的使用与输出结果,如下所示:

  

三、ReactiveSwift中的Event的实现

接下来我们就来分析一下ReactiveSwift框架中的Event枚举的代码实现。我先看其源码,然后再看其使用方式。

1、Event中的事件类型

下方截图中就是Event枚举类型中所包含的所有枚举项。从下方代码中我们可以看出,Event后方跟了两个泛型,一个是Value,另一个是遵循Swift.Error协议的Error泛型。然后紧跟着的是Event枚举中的几个事件类型。下方是对这几种类型的介绍:

  • value: 用来关联信号量所传送过来的值,该值的类型就是上面定义的Value泛型。

  • failed: 表示因错误而被迫中止的事件,其关联值是相关的错误信息。

  • completed: 该事件是完成事件,也就是所有的东西都success,正常终止。

  • interrupted: 该事件表示被迫中断的事件,也就是没有达到预期效果,被迫中止。

  

2、Event中的 isCompleted 和 isTerminating计算属性

这两个属性是计算属性,下方是其实现代码。isCompleted 用来判断该事件是否是正常完成的事件,而isTerminating主要用来判断事件是否已经终止,当然其中包括异常终止。当然这两个计算属性也是比较简单的,就是根据不同的条件返回不同Bool值即可。

  

 

3、Event中的 value 和 error 计算属性

下方这两个也是计算属性,主要是通过 if-case-let 语句来获取枚举的关联值,并与相应的计算属性进行关联。value属性则用来获取枚举项.value所关联的值。而error则用来获取枚举项.failed所关联的值。具体代码如下所示。

  

 

4、Even计算属性的测试

接下来,我们就对上述的计算属性进行测试。下方这段代码就是对上述计算属性的测试。首先我们创建了一个类型为 Event<Int, NSError>类型的事件。该事件所关联的值为100,然后我们输出计算属性value、isTerminating、isCompleted计算属性的值进行打印,具体打印结果如下所示。

然后我们又创建了一个错误类型的事件errorEvent。并给该枚举项关联一个NSError类型的错误对象。然后对error、isTerminating、isCompleted的值进行打印。从打印结果可以看出isTerminating为true,说明是终止事件,而isCompleted为false,则说明是非正常终止。

  

 

5、Event中的map函数

在Event枚举中,主要有两个map函数,一个是map<U>()泛型函数。另一个是mapError<F>()泛型函数。因为mapError<F>()函数的实现与map<F>()函数的实现极为相似,我们此处就以mapError<U>()泛型函数为例。也就是下方这个完整的函数。

map<U>()函数是一个泛型函数,在函数名map后紧跟的<U>就是我们定义的泛型。而该函数的参数是一个闭包 f, 该闭包的类型为 (Value) -> U。也就是说该闭包的有一个Value类型的参数,并且返回一个U类型的返回值。map<U>()这个函数的返回值是一个新的事件,该事件的类型为Event<U, Error>。经过这么一分析,map<U>()函数就是将当前的 Event<Value, Error> 类型的事件映射成Event<U, Error>类型的事件。当然此处的Value和U都是泛型,当然如果换成具体的参数的话,也就是说一个 Event<Int, Error> 类型的参数可以通过下方的方法来映射成 Error<String, Error> 类型的事件。

下方我们需要主要的是返回值 .value( f(value) ) 这句话,.value()的关联值是f(value)这个闭包所返回的值,而f(value)这个闭包的参数是之前事件所绑定的值。而f(value)所返回的值就是要映射的结果类型。f()的闭包体由用户来提供,也就是说用户可以自定义映射规则。

  

 

6、map函数的测试用例

接下来我们来看一下Map函数的使用方式。下方代码段就是Map函数的测试用例以及运行结果。首先我们创建了一个类型为 Event<Int, NSError> 类型的事件,然后该事件的value值为100。 然后我们调用map函数将 Event<Int, NSError> 类型映射成 Event<String, NSError>类型。然后map函数后边跟随的尾随闭包就是我们的映射规则。你可以在该闭包中添加任意的映射规则,将原来的值转换成你想要的值。

  

mapError<F>()函数的实现以及使用方式,与上述函数类似。接下来我们就来看一下mapError<F>()函数的使用方式。首先我们定义了两个错误类,一个是MyError另一个是MyError1。并且定义了一个Event<Int, MyError> 类型的错误事件,然后调用 mapError<F>()函数将其转换成 Event<Int, MyError1> 类型的事件,当然调用时提供的闭包仍然是映射规则。具体如下所示。

  

Event枚举中还有对 == 号运算符的重载,使Event类型的参数支持 == 运算符。其中还有一个将事件类型转换成description描述字符串的 extension。因为其内容比较简单,在此就不做过多赘述了。

四、ReactiveSwift中的Observer

聊完Event的实现,我们来看一下Observer类的实现。Observer的主要职能是对Event进行使用,也就是Observer可以调用自己的方法来发送Event中所提供的各种事件的。下方就是对Observer类的详细解析。

1、Observer类中属性以及构造器的解析

接下来我们来看一下Observer类中所声明的属性以及构造器。首先我们注意到,Observer类也是也一个泛型类,在Observer类名后方分别跟着 ValueError: Swift.Error两个泛型。这两个泛型分别与Event后边的泛型相对应,Value就是事件所关联值的类型,而Error就是发生错误时错误的类型。

紧接着是声明了一个 (Event<Value, Error>) -> Void 的闭包类型,并且为该类型声明了一个Action的别名。然后使用这个Action的别名声明了一个action的不可变属性。而Observer的构造器的参数就是一个类型为(Event<Value, Error>) -> Void 的闭包。

Observer还声明了一个便利构造器。该便利构造器有四个可选类型的参数,每个参数的类型都是一个闭包。这四个可选类型的闭包参数分别与Event中的四种事件相对应,在便利构造器中调用Observer的构造器时,提供了Action闭包的闭包体,在Action闭包体中,根据具体的事件类型来执行便利构造器参数所提供的相应闭包参数。当然便利构造器的闭包参数由Observer的使用者所提供,用来回调相应事件中的值。

  

根据上面的源代码我们不难看出,在初始化Observer的对象时,我们可以调用构造器,也可以调用便利构造器来进行初始化。当然,还是推荐使用便利构造器来实例化Observer类的实例。下方第一个就是使用的便利构造器来实例化Observer的,并且在调用是提供了四个闭包回调,来分别处理Observer发来的不同事件。

当然你也可以直接调用 Observer所提供的构造器,也就是直接为Action闭包赋值。第二段代码中的尾随闭包就是Action的闭包体,当然我们需要自己处理Action针对不同事件是所给出的处理块。

  

2、Observer中发送事件的方法sendXXX()

接下来我们就来看一下Observer中发送各种事件的方法,当然Event有四种事件类型,那么Observer中也就是是4个发送事件的方法了。下方代码片段就是Observer中发送事件的方法,从下方的方法中我们不难看出,发送事件其实就是对 action闭包的调用,并且传入相应的事件。在调用 action 闭包时,就会执行我们所提供的或者遍历构造器中所提供的闭包体,将发送的事件回调出去。

  

3、sendXXX()方法的测试用例

上面我们已经通过Observer的构造器和便利构造器实例化两个实例,接下来我们就调用这些实例所对应的send方法。下方代码片段就是对相应Observer实例的相关send方法的执行。

  

下方就是上述测试用例的执行结果

  

五、Observer工作的流程图

看完上述代码,因为闭包回调会导致一些代码的执行流程已经调用关系不太容易理解,解析来我们就来画一个图来简述Observer的具体工作过程。下方代码就是上述测试用以的执行以及调用的过程。

下方是一个完整的程序执行过程,输入->处理->输出

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

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

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #de38a5 }
span.s1 { }
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: #ffffff }
span.s1 { }

时间: 2024-11-05 12:06:45

ReactiveSwift源码解析(一) Event与Observer代码实现的相关文章

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

为了之后博客的进行,本篇博客我们就来聊一下ReactiveSwift框架中的Lifetime类的具体实现.从Lifetime这个名字中我们就这道,就是生命周期.在ReactiveSwift中使用Lifetime来标记一个对象的生命周期,其实主要功能还是将对象的deinit()析构函数通过发送信号量将其回调出来.接下来我们就来看一下Lifetime类的实现.Lifetime类与Event和Observer相似,也是比较原子性的类,以原子组件的形式存在于ReactiveSwift中. 下方我们会先给

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

上篇博客我们聊完SignalProducer结构体的基本实现后,我们接下来就聊一下SignalProducerProtocol延展中的start和lift系列方法.SignalProducer结构体的方法扩展与Signal的扩展相同,都是面向协议的扩展.首先创建了一个SignalProducerProtocol协议,使SignalProducer在延展中遵循SignalProducerProtocol协议.然后我们再对SignalProducerProtocol进行扩展.这样一来,SignalP

ReactiveSwift源码解析(十一) Atomic的代码实现以及其中的Defer延迟、Posix互斥锁、递归锁

本篇博客我们来聊一下ReactiveSwift中的原子性操作,在此内容上我们简单的聊一下Posix互斥锁以及递归锁的概念以及使用场景.然后再聊一下Atomic的代码实现.Atomic主要负责多线程下的原子操作,负责共享资源的同步一致性.而在Atomic中就是使用到了Posix互斥锁和递归锁.在聊上述内容之前,我们先来回顾一下Swift语言中延迟执行defer的使用方式,在之前Swift编程的相关博客中也涉及到了defer的使用方式.defer因为Atomic使用到了延迟操作,所以下方我们再做一个

Redis源码解析:19Hiredis异步API代码解析

Hiredis中的异步API函数需要与事件库(libevent,libev, ev)一起工作.因为事件循环的机制,异步环境中的命令是自动管道化的.因为命令是异步发送的,因此发送命令时,必要情况下,需要提供一个回调函数,以便在收到命令回复时调用该函数. 异步API涉及到的函数分别是: redisAsyncContext *redisAsyncConnect(const char *ip, int port); int redisAsyncCommand(redisAsyncContext *ac,

vue系列---响应式原理实现及Observer源码解析(一)

_ 阅读目录 一. 什么是响应式? 二:如何侦测数据的变化? 2.1 Object.defineProperty() 侦测对象属性值变化 2.2 如何侦测数组的索引值的变化 2.3 如何监听数组内容的增加或减少? 2.4 使用Proxy来实现数据监听 三. Observer源码解析 回到顶部 一. 什么是响应式? 我们可以这样理解,当一个数据状态发生改变的时候,那么与这个数据状态相关的事务也会发生改变.用我们的前端专业术语来讲,当我们JS中的对象数据发生改变的时候,与JS中对象数据相关联的DOM

PhotoView 源码解析

PhotoView 源码解析 本文为 Android 开源项目源码解析 中 PhotoView 部分项目地址:PhotoView,分析的版本:48427bf,Demo 地址:PhotoView-demo分析者:dkmeteor,校对者:cpacm,校对状态:完成 1. 功能介绍 特性(Features): 支持Pinch手势自由缩放. 支持双击放大/还原. 支持平滑滚动. 在滑动父控件下能够运行良好.(例如:ViewPager) 支持基于Matrix变化(放大/缩小/移动)的事件监听. 优势:

Caffe2源码解析之core

写在前面 在对Tensorflow的后端源码进行了拆解(参见tensorflow源码解析系列文章索引)之后,很想跟其它深度学习框架的实现进行对比,根据框架的流行程度,先选择了Pytorch.Pytorch的后端核心是直接复用了Caffe2,因此本文针对Caffe2源码的core模块进行了简单拆解. 目录 数据存储与表示 storage tensor blob qtensor 操作 observer observable operator 操作求导 operator_schema context

Flume-ng源码解析之Channel组件

如果还没看过Flume-ng源码解析之启动流程,可以点击Flume-ng源码解析之启动流程 查看 1 接口介绍 组件的分析顺序是按照上一篇中启动顺序来分析的,首先是Channel,然后是Sink,最后是Source,在开始看组件源码之前我们先来看一下两个重要的接口,一个是LifecycleAware ,另一个是NamedComponent 1.1 LifecycleAware @[email protected] interface LifecycleAware {  public void s

socketserver源码解析和协程版socketserver

来,贴上一段代码让你仰慕一下欧socketserver的魅力,看欧怎么完美实现多并发的魅力 client import socket ip_port = ('127.0.0.1',8009) sk = socket.socket() sk.connect(ip_port) sk.settimeout(5) while True: data = sk.recv(1024) print('receive:',data.decode()) inp = input('please input:') sk