iOS开发之Alamofire源码解析

时间:2020-12-26 22:24:39

今天博客中的Alamofire源码的版本是以3.4版本为例。上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看《详解NSURLSession》,为了就是给本篇博客打下基础。因为AlamoFire就是对NSURLSession及其相关的东西进行了进一步的封装,让网络请求使用起来更为简单。本篇博客就详细的来窥探一下AlamoFire源码,主要来看一下AlamoFire是如何对NSURLSession进行封装的,并且来看一下在封装时使用了哪些Swift语言中的高级用法,也就是看一下Swift语言进一步的使用方法。

当然AlamoFire是AF的Swift版本了,其中虽然是使用Swift语言实现的,但是实现思路与AFNetWorking大同小异。如果你之前阅读过AFNetWorking的源码,那么你对Alamofire的源码应该并不陌生,好多地方的实现思路是一致的。今天博客中不是教你如何去使用AlamoFire,而是告诉你Alamofire是如何实现的。本篇博客中的干货还是比较足的,关于AlamoFire的官方文档请移步Alamofire -- github链接

下方第一部分的类图是在阅读AlamoFire源码时为了梳理每个结构间的关系画的一个简单的类图,下方的类图没有涵盖AlamoFire中所有的类,而是给出了核心部分的模块以及各个模块间的关系。接下来我们将对下方的模块进行拆分,然后各个击破。本篇博客的主题思路是先整体的看一下AlamoFire的组织架构,并解析每部分的关系。然后对每个模块进行详细的解析,在解析时我们会对一些Swift语言的知识点进行提取,然后将其进行剖析。

一.Alamofire核心模块概述

我们先整体上来看一下AlamoFire这个框架关系,概述一些核心模块。该部分我们先来看一下AlamoFire的文件组织结构,然后在给出这些文件组织结构中类的关系。所以在本部分类图是少不了的。废话少说,进入该部分的主题。

1.Alamofire的目录结构解析

首先我们来看一下AlamoFire的目录结构,从整体上来把控一下AlamoFire。下方截图是AlamoFire框架的所有文件,文件不算多,Alamofire框架的源代码并不算多,所有理清Alamofire的框架结构还是不难的。下方截图中是AlamoFire中的所有文件,Core文件夹下是Alamofire的核心文件,Features主要是对核心文件的扩展。今天我们就以核心文件为主,Feature文件为辅来窥探一下AlamoFire框架的源码。

下方是对Core文件夹下的各个文件的功能简述:

  • Alamofire.swift ---- 该文件中主要是给用户提供一些便利的调用方法,用户可以直接调用该文件中的便利方法来使用Alamofire相关功能。
  • Manager.swift ---- Manager中定义了Session对象,Session相关的Delegate,以及Delegate执行的队列等相关信息,在Manager中创建Request对象发起请求。Manager管理的就是各种请求,Manager对象是以单例的形式对外开放的。
  • Request.swift ---- 该文件如其名,就是负责创建Session的各种task的,并执行相关的SessionTask,并调用相关书籍解析的功能模块对数据进行解析并通过回调返回给用户。
  • ParameterEncoding.swift ---- 负责请求参数的各种编码(URL、URLEncodedInURL、JSON、PropertyList等编码),并将编码后的数据与URLRequest结合后的结果进行返回。
  • Result.swift ---- 对解析后的数据封装成Result对象。
  • Response.swift ---- 负责将服务器相应的数据进行封装生成Response对象,该对象中就包括上述的Result对象,用户最终会通过闭包回调的形式获取到该Response的对象。
  • Notifications.swift ---- 其中是一个Notification结构体,该结构体中定义了一些字符串,这些字符串就是所需通知的Key,当网络请求DidResume、DidSuspend、DidCancel、DidComplete都会发出通知。
  • Error.swift ---- 其中是一个Error的结构体,其中封装的是各种错误状态。

Features文件夹下各个文件的功能简述:

  • Download.swift ----- 对Manager和Request类进行扩展,使其支持Down Task,其中封装了NSURLSessionDownloadDelegate相关代理方法。
  • Upload.swift ---- 在该文件中也是对Manager和Request类进行的扩展,使其支持Upload Task,其中封装了NSURLSessionDataDelegate中获取上传数据进度的代理方法,也就是taskDidSendBodyData代理方法。
  • MultipartFormData.swift ---- 该文件从名字就可以看出是为了组织多表单数据上传的数据的,在Upload Task中就使用到了MultipartFormData。
  • Stream.swift ---- 和Download和Upload文件相似,该文件中也是对Manager和Rquest做延展,主要使其支持数据流的传输,其中主要封装和实现了NSURLSessionStreamDelegate相关的代理方法。
  • ResponseSerialization.swift ---- 该文件中主要是对Request类进行数据解析的延展的。其中封装了各种对响应数据的解析方式,其中包括Data、String、JSON、PropertyList等解析方式。
  • NetworkReachabilityManager.swift ---- 该文件主要是对SystemConfiguration.framework中的SCNetworkReachability相关的东西进行封装的,主要用来管理和监听网络状态的变化。
  • ServerTrustPolicy.swift ---- 这个文件主要是对NSURLSession做的延展,其中定义了各种网络请求的认证策略,主要证书认证相关东西。
  • Timeline.swift ---- 该文件是为了方便调试而生的,其中记录了相关操作的时间点,并且对其进行记录,便于在Debug时使用到。
  • Validation.swift ---- 主要是用来验证请求是否成功,如果出错了就做相应的处理。

上面是AlamoFire中所有文件的概述,上面这些算是对AlamoFire框架有大概的了解吧。结合上方的概述,来研读AlamoFire源码还是比较清晰的。下方就是AlamoFire 3.4中相关文件的木头结构,如下所示:

  iOS开发之Alamofire源码解析

2.核心类间的结构

上面简单的介绍了Alamofire框架的目录结构以及每个文件所负责的内容。接下来我们进入到各个文件的内部,来整体的看一下核心类之间的关系。下方是核心类的“类图”,当然下方只是核心类的。接下来来概述一下下方的类图,因为下图太大,在此看起来不太清楚,如果你感兴趣,你可以另存为,然后放大查看。

  • 下方黑框中的部分对应的就是Alamofire.swift中的内容。其中主要是一些URL转换字符串的延展以及URLRequst转换成MutableURLRequest的延展,还有一些用户使用的便利方法。
  • 黄框中就是我们Manager.swift中的内容了,Manager类的对象是以单例的形式对外展现的,上述黑框中的便利方法,主要是获取Manager类的单例,然后调用相应的方法。
  • 绿框中所对应的主要是Request.Swift和Features文件夹中的内容,主要是Request类及其延展,当然还有对Manager和NSURLSession的延展。
  • 红框中的就是网络请求会话的各种任务的回调方法的封装了,在这些回调方法中提供了默认实现,并对外留有回调块,以便让用户来自己实现这些回调方法。

  iOS开发之Alamofire源码解析

二、Alamofire.swift源码解析

第一部分算是概览了一下Alamofire框架中的各个组成部分,接下来该进入到上述的各个模块中来进一步来窥探其实现和组织方式了。还是“顺藤摸瓜”,先从用户看的到的地方着手,然后层层深入,直到你看不见的地方。所以第二部分我们先来看一下Alamofire.swift中的内容,因为该文件是Alamofire框架的入口。

1.Alamofire.swft中的类图结构

下方的类图就是第一部分类图中黑框的放大版,根据Alamofire.swift这个文件我们不难画出下方这个类图。有一点要说明的就是在类图中省略了一些遍历方法,只写了一些主要的,不过核心的功能还是有的。下方的URLStringConvertible和URLRequestConvertible是负责类型转换的接口,具体的请看下方的介绍。该文件中除了类型转换的方法外就是一些调用Manager的单例的便利方法了。

  iOS开发之Alamofire源码解析

2.Alamofire.swift技术细节

下方就是URLStringConvertible协议以及相关延展的具体实现,主要功能就是将String、NSURL、NSURLComponents、NSURLRequest中的URL转换成字符串类型。将要转换的类型要遵循URLStringConvertible协议,并在计算变量URLString中返回转换后的字符串。具体做法如下所示,这中类型转换方式在开发中经常会使用到,下方会给出其他实例。URLRequestConvertible协议的功能与URLStringConvertible大同小异,URLRequestConvertible协议的实现者负责将NSURLRequest转换成NSMutableURLRequest类型。在此就不做过多的赘述了。

  iOS开发之Alamofire源码解析

下方截图是Alamofire.swift中的一个便利方法,其他几个便利方法与此相似,都是调用Manager单例中相应的方法,便利方法为了链式调用Request类中的相关方法,所以所有的便利方法都会返回当前Manager单例使用的Request对象。具体如下所示:

  iOS开发之Alamofire源码解析

3.扩展用例

学以致用,举一反三。上面那种“面向协议”开发的思想值得我们学习,之前在设计模式相关的系列博客中不止一次的提到过要“面向接口编程”,此处的协议就是接口。虽然上面只是使用协议来进行简单的类型转换,这种思想是非常值得我们学习的。通过上面类型转换的方式,我们可以写出下方代码。下方代码不是Alamofire框架中的代码,是我根据上述的类型转换的实例所实现的,下方定义了一个类型转换的协议,需要转换的类型要遵循这个协议,下方以String为例,具体做法如下所示。

  iOS开发之Alamofire源码解析

三、Manager.swift源码解析

因为便利方法主要是调用的Manager类的单例,所以接下来我们来看Manager.swift中的东西。Manager类中主要负责Session和Request的初始化,并且提供SessionDelegate代理方法的默认实现。在实现代理方法时留出了相应的闭包已提供给用户使用该闭包来回调相应的代理方法。在Manager中的SessionDelegate类就是NSURLSessionDelegate以及相关子协议的代理类 ,其中就给出了各个代理方法的默认实现,在实现时并定义了一系列的Closure回调变量,当这些闭包变量不为空时就会执行闭包块中的内容,而不会执行提供的默认实现。

1.Manager.swift相关类图

下方类图就是黄色部分的放大版,主要是Manager类与SessionDelegate的关系。从下方类图中不难看出,SessionDelegate类遵循了NSURLSessionDelegate协议以及子协议,并给出了代理相应的实现方法。下方的代码会给出代理的具体封装和实现方式。

  iOS开发之Alamofire源码解析

2.Manager类的相关属性

开门见山,因为Manager类对外是以单例的形式对外使用的,所以我们先来看看Manager类的单例实现。下方截图中的sharedInstance计算属性就是Manager的单例,其中存储的就是一个Manager对象,在创建Manager对象时我们为Manager对象中存储的NSURLSession对象指定了一个defaultSessionConfiguration和一个defaultHTTPHeaders。

  iOS开发之Alamofire源码解析

上面是Manager类中单例的实现,接下来我们来解析类中核心的属性,下方是一些核心属性的解析:

  • defaultHTTPHeaders属性 : defaultHTTPHeaders是Manager类中的一个计算属性,负责组织默认的请求Header中的内容。
  • session属性: 该属性的类型是NSURLSession类型的,负责请求会话,并创建各种会话任务。
  • queue属性:该属性是一个串行队列,该队列负责执行session创建Session Task的任务。
  • delegate属性:该属性是SessionDelegate类型的,而SessionDelegate类遵循了NSURLSessionDelegate及其子协议,并给出了相应的实现,在下方会着重介绍SessionDelegate。而此处的delegate属性负责调用SessionDelegate类中相应的回调方法。

3.Manager类的request方法

在Manager.swift源文件中给出了request方法的实现,Manager类的单例所调用的upload、download等方法是在其他源文件中做的延展。那些延展中的方法稍后在聊,本部分中就先对request方法进行解析。下方的方法就是我们在便利方法中使用Manager类的单例所调用的方法。method参数表示请求方式(GET, POST, PUT等),URLString参数是请求地址,parameter就是请求参数了。encoding参数就是请求参数的编码方式,此处默认是URL编码。headers字典参数就是请求头信息了,默认为nil。下方代码主要是创建NSMutableURLRequest对象,然后将参数进行相应的编码后添加进NSMutableURLRequest对象中,然后调用request()方法发起请求。

  iOS开发之Alamofire源码解析

下方代码段是上述函数中所调用的request()方法,下方的request()方法负责通过Session创建dataTask,也就是负责执行Data Task任务。然后在初始化Request类的对象时,将创建的Data Task对象传给Request对象。然后将Request对象的 Task Delegate对象存入Manager类的delegate属性中。因为在delegate属性中的代理方法是调用相应的Task Delegate的方法,所以在此有必要进行存储。然后调用Request对象的resume()方法发起数据的网络请求。为了链式调用Request对象的其他方法,所以将Request类的对象进行返回。代码如下所示:

  iOS开发之Alamofire源码解析

4. Manager中SessionDelegate解析

SessionDelegate可以说是代理的代理,因为在SessionDelegate中有一个subdelegates字典属性,该属性负责存储Request对象中的各个Task Delegate。而SessionDelegate在相应的代理方法中会通过存储的Task Delegate来调用Task Delegate中的方法,所以SessionDelegate说是代理的代理。

下方代码段是SessionDelegate类中部分代码的截图,其中的subdelegates字典属性中存储的就是Request中的TaskDelegate,subdelegateQueue是一个并行队列用来同步执行获取和设置字典中的Task Delegate对象。然后就是为SessionDelegate类定义了一个下标,该下标的功能是以Session Task为下标的形式向subdelegates中添加和获取相应的Task Delegate。该自定义下标就可以让类的对象使用下标的形式来设置和获取属性的值,稍后会给出扩展的Demo。

  iOS开发之Alamofire源码解析

关于SessionDelegate中所实现的代理方法,在此我们就一执行Data Task请求的didReceiveData代理方法为例。下方截图就是SessionDelegate中的didReceiveData代理方法。代码比较简单,首先判断该代理方法对象的Closure回调变量是否有值,如果有就执行该闭包回调块,如果没有值就获取我们存储的Data Task Delegate, 然后去执行Data Task Delegate中的didReciveData方法。其他方法也于此类似,所以就以点代面,在此就不做过多的赘述了。

iOS开发之Alamofire源码解析

5.知识点扩展

接下来有到了举一反三,扩展知识点的时刻了。接下来我们单独来创建一个小实例来看一下Swift中自定义下标是怎么回事。大道至简,接下来我们将上述下标的使用进行简化,创建一个Demo, 然后通过这个Demo来介绍一下下标的使用。

下方代码段就是我们创建的简化版的下标示例,在Swift的类中是支持自定义下标的,自定下标可以让你以下标的形式来访问和设置属性。下方就自定义了一个下标,在下标中设置和返回value属性的值。用法如下所示:

  iOS开发之Alamofire源码解析

四、Request.swift源码解析

逐渐深入,我们现在来到了Request.swift这个类,因为上面的Manager中的请求最终走到了Request类的位置,所以接下来我们要分析的就是Request.swift源文件中的内容。Request.swift源文件中主要是执行的Data Task请求,并且实现了相应的Data Task Delegate中的方法。其他的任务例如Download Task, Upload Task,Stream Task等会在其他文件中对Request做延展时执行上述这些任务。我们在此就以Data Task为例。Request类中说白了就是负责通过会话创建相应的Task,并实现相应Task的代理方法。

1. Request相关类图

下方类图就是Request相关类图了,Request类及其延展中就是创建各种类型的Task,然后给出相应的Task Delegate。下方类图还给出个各种Task Delegate间的继承关系。Request相关源文件在给出TaskDelegate的代理方法的实现时,也封装了闭包状态下的回调方法。这中做法与SessionDelegate中做法一致。下方会给出具体的介绍。

  iOS开发之Alamofire源码解析

2.Request类的初始化方法

下方就是Request类的初始化方法,方法需要两个参数,第一个参数是NSURLSession的对象,该对象也就是Manager单例中创建的Session的对象。而第二个参数虽然是Manager传过来的,但是初始化task的任务还得交给Request类来做,Manager来只不过是定义了一个NSURLSessionTask的类型传到了Request中,例如在Manager的request()方法中task是NSURLSessionDataTask,Manager的upload()方法中的task是NSURLSessionUploadTask类型。

在Request的初始化方法中根据Manager单例提供的task的类型来确定是创建DataTaskDelegate、UploadTaskDelegate等。在相应的Task Delegate中会创建相应的Task。我们还以上述的DataTask为例,如果你调用Manager单例中的request()方法就会执行下方的DataTaskDelegate()的初始化。如下所示:

  iOS开发之Alamofire源码解析

上面的delegate是TaskDelegate类型的,因为UploadTaskDelegate、DownloadTaskDelegate以及DataTaskDelegate都是TaskDelegate的子类,所以此处用到了面向对象的“多态性”。下方两个属性就是Request类中的delegate和task属性,delegate的初始化在上述Requset的初始化方法中,而此处的task是一个存储属性,task的初始化是放在相应的TaskDelegate中的,在TaskDelegate中创建完task对象后再赋值给Request类中的task属性,如下所示:

  iOS开发之Alamofire源码解析

3. Requset类中的Progress闭包

在使用Request类的对象时,我们可以链式的调用Request中的方法,最常用的就是获取相应任务执行的进度,也就是平时我使用的progress()方法。下方截图中的代码段就是progress()方法的实现。通过Task Delegate的类型来判断目前执行的哪种任务,然后将传过来的progress的闭包赋值给相应的Task Delegate,在这些Task的Delegate中会在相应的回调方法中获取任务执行进度,然后执行下方传入的Closure。

  iOS开发之Alamofire源码解析

4.Request的resume()方法

下方就是Request类中的resume()方法,其中的代码比较简单。主要是用来记录startTime,然后调用task的resume方法开始执行任务。当然在开始执行任务后要发起相应的通知,此处发出的是DidResume通知。所有的通知类型都在Notifications.swift文件中的Notifications结构体中存储着。Request类中的其他方法,比如suspend()、cancel()方法的实现方式与resume()类似,并且都会发出相应的通知,在此就不做过多的赘述了。

  iOS开发之Alamofire源码解析

5.Request类中的相关代理类

从第一部分中的类图中我们能看出与Request类相关的代理类,TaskDelegate是所有代理类的基类。在该代理类中其实就是定义了一下必要的属性和NSURLSessionTaskDelegate中对应的回调方法,并且为这些回调方法提供相应的闭包回调的形式。此处就以TaskDelegate代理类为例。下方就是TaskDelegate代理类为NSURLSessionTaskDelegate中相应的代理方法提供的Closure方式。其他的代理类如DataTaskDelegate、DownloadTaskDelegate等与此类似。而相应的代理方法中就是对回调进行了处理,不过在处理之前会判断相应的Closure是否为nil, 如果不为nil的话就执行Closure闭包块中的内容。如果为nil,就执行提供的默认处理。

  iOS开发之Alamofire源码解析

事无巨细,至此Alamofire中的核心类就已经介绍完毕,因为篇幅有限,其他类在此就不做过多赘述了。其他类以及其他文件中的内容在第一部分中做了概述,其内部的实现细节就不做过多赘述了,在Github上分享的代码对这些类的关键技术细节给出了注释。

在Alamofire框架中大量的使用了延展、闭包以及枚举关联值。特别是在解析网络请求的数据时,将闭包类型作为函数的参数,然后通过闭包变量来提供相应的解析方案,在此就不做过多的赘述了,其他技术细节“仁者见仁,智者见智”。听我说再多,看再多的技术博客如果不亲自的去了解一下,说再多也是没用的,实践出真知。关于Alamofire源码的其他内容在此就不做过多赘述了,如果感兴趣就亲自的去阅读吧,欢迎互相交流。今天博客就先到这儿。

github分享链接:https://github.com/lizelu/iOS_NetWorkingAndAlamofire