本篇主要带来Alamofire中Response的解读

前言

在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下。我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然,这些理解也都是基于我自己的理解。难免具有局限性。

当我们设计完一个Request的时候,我们肯定要处理服务器返回的响应数据。在Alamofire源码解读系列(一)之概述和使用中,我们已经讲过,Alamofire中把Request分为了4类:

  • DataRequest
  • DownloadRequest
  • UploadRequest
  • StreamRequest

Alamofire中Request可以使用链式访问:

Alamofire.request("https://httpbin.org/get").responseString { response inprint("Response String: \(response.result.value)")}.responseJSON { response inprint("Response JSON: \(response.result.value)")}

能实现链式访问的原理就是每个函数的返回值都是Self。那么在上边的代码中,虽然response的名字都一样,但并不是同一类型。

因为有4中不同的Request类型,StreamRequest我们先不提,对于UploadRequest来说,服务器响应的数据比较简单,就响应一个结果就行,因此不需要对它的Response专门进行封装。因此,Alamofire设计了2中与之相对应的Response类型,他们分别是:

  • DefaultDataResponse / DataResponse
  • DefaultDownloadResponse / DataResponse

那么,如果我们使用下边的代码获取响应数据:

// Response Handler - Unserialized Response
func response(queue: DispatchQueue?,completionHandler: @escaping (DefaultDataResponse) -> Void)-> Self

获取的是没有经过序列化后的数据,如果使用了没有序列化的response方法,返回的就是带有Default开头的响应者,比如DefaultDataResponse,DefaultDownloadResponse。如果使用了序列化的response方法,返回的就是DataResponse或者DataResponse。

这说明了什么? 在程序的设计层面上,这种前后呼应的手法能够让代码更好理解。就像在项目中不能把所有的参数都放到一个模型中一样。

拿DefaultDataResponse / DataResponse来举例,DataResponse基本上只比DefaultDataResponse多了一个系列化后的数据属性。

还有一点要提一下,先假设每个Request都可以被序列化为JSON,或者String。这些都属于序列化Response的范围。序列化成功后,保存数据的容器应该有一个类型,而这个类型又是可以变化的,在本篇文章下边的内容中会指出泛型的使用方法。

DefaultDataResponse

DefaultDataResponse用于存储data或upload请求情况下的所有无序列化的数据。那么在Alamofire中对于服务器的响应主要关心的数据有下边几个:

  • request: URLRequest? 表示该响应来源于那个请求
  • response: HTTPURLResponse? 服务器返回的响应
  • data: Data? 响应数据
  • error: Error? 在请求中可能发生的错误
  • timeline: Timeline 请求的时间线封装,这个会在后续的文章中解释
  • _metrics: AnyObject? 包含了请求和响应的统计信息

代码如下:

 /// The URL request sent to the server.public let request: URLRequest?/// The server's response to the URL request.public let response: HTTPURLResponse?/// The data returned by the server.public let data: Data?/// The error encountered while executing or validating the request.public let error: Error?/// The timeline of the complete lifecycle of the request.public let timeline: Timelinevar _metrics: AnyObject?

一般来说,在swift中,如果只是为了保存数据,那么应该把这个类设计成struct。struct是 值传递,因此对数据的操作更安全。除了定义需要保存的数据属性后,必须设计一个符合要求的构造函数。

 /// Creates a `DefaultDataResponse` instance from the specified parameters.////// - Parameters:///   - request:  The URL request sent to the server.///   - response: The server's response to the URL request.///   - data:     The data returned by the server.///   - error:    The error encountered while executing or validating the request.///   - timeline: The timeline of the complete lifecycle of the request. `Timeline()` by default.///   - metrics:  The task metrics containing the request / response statistics. `nil` by default.public init(request: URLRequest?,response: HTTPURLResponse?,data: Data?,error: Error?,timeline: Timeline = Timeline(),metrics: AnyObject? = nil){self.request = requestself.response = responseself.data = dataself.error = errorself.timeline = timeline}

DataResponse

DataResponse

/// Used to store all data associated with a serialized response of a data or upload request.
public struct DataResponse<Value> {/// The URL request sent to the server.public let request: URLRequest?/// The server's response to the URL request.public let response: HTTPURLResponse?/// The data returned by the server.public let data: Data?/// The result of response serialization.public let result: Result<Value>/// The timeline of the complete lifecycle of the request.public let timeline: Timeline/// Returns the associated value of the result if it is a success, `nil` otherwise.public var value: Value? { return result.value }/// Returns the associated error value if the result if it is a failure, `nil` otherwise.public var error: Error? { return result.error }var _metrics: AnyObject?/// Creates a `DataResponse` instance with the specified parameters derived from response serialization.////// - parameter request:  The URL request sent to the server./// - parameter response: The server's response to the URL request./// - parameter data:     The data returned by the server./// - parameter result:   The result of response serialization./// - parameter timeline: The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.////// - returns: The new `DataResponse` instance.public init(request: URLRequest?,response: HTTPURLResponse?,data: Data?,result: Result<Value>,timeline: Timeline = Timeline()){self.request = requestself.response = responseself.data = dataself.result = resultself.timeline = timeline}
}

DataResponse: CustomStringConvertible, CustomDebugStringConvertible

DataResponse实现了CustomStringConvertible和CustomDebugStringConvertible协议,因此可以自定义DataResponse的打印信息。

我们也可以给我们模型实现这两个协议,在代码调试的时候,打印出详细的信息,比打断点来查看效率更高。

extension DataResponse: CustomStringConvertible, CustomDebugStringConvertible {/// The textual representation used when written to an output stream, which includes whether the result was a/// success or failure.public var description: String {return result.debugDescription}/// The debug textual representation used when written to an output stream, which includes the URL request, the URL/// response, the server data, the response serialization result and the timeline.public var debugDescription: String {var output: [String] = []output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")output.append("[Data]: \(data?.count ?? 0) bytes")output.append("[Result]: \(result.debugDescription)")output.append("[Timeline]: \(timeline.debugDescription)")return output.joined(separator: "\n")}
}

DefaultDownloadResponse

DefaultDownloadResponse保存的是下载任务的数据。有3个属性需要做一下介绍:

  • temporaryURL: URL? 现在成功后,数据会被保存在这个临时URL中
  • destinationURL: URL? 目标URL,如果设置了该属性,那么文件会复制到该URL中
  • resumeData: Data? 表示可恢复的数据,对于下载任务,如果因为某种原因下载中断了,或失败了,可以使用该数据恢复之前的下载

其他的内容跟上边介绍的内容没什么特别的地方,就简单的把代码弄上来了:

/// Used to store all data associated with an non-serialized response of a download request.
public struct DefaultDownloadResponse {/// The URL request sent to the server.public let request: URLRequest?/// The server's response to the URL request.public let response: HTTPURLResponse?/// The temporary destination URL of the data returned from the server.public let temporaryURL: URL?/// The final destination URL of the data returned from the server if it was moved.public let destinationURL: URL?/// The resume data generated if the request was cancelled.public let resumeData: Data?/// The error encountered while executing or validating the request.public let error: Error?/// The timeline of the complete lifecycle of the request.public let timeline: Timelinevar _metrics: AnyObject?/// Creates a `DefaultDownloadResponse` instance from the specified parameters.////// - Parameters:///   - request:        The URL request sent to the server.///   - response:       The server's response to the URL request.///   - temporaryURL:   The temporary destination URL of the data returned from the server.///   - destinationURL: The final destination URL of the data returned from the server if it was moved.///   - resumeData:     The resume data generated if the request was cancelled.///   - error:          The error encountered while executing or validating the request.///   - timeline:       The timeline of the complete lifecycle of the request. `Timeline()` by default.///   - metrics:        The task metrics containing the request / response statistics. `nil` by default.public init(request: URLRequest?,response: HTTPURLResponse?,temporaryURL: URL?,destinationURL: URL?,resumeData: Data?,error: Error?,timeline: Timeline = Timeline(),metrics: AnyObject? = nil){self.request = requestself.response = responseself.temporaryURL = temporaryURLself.destinationURL = destinationURLself.resumeData = resumeDataself.error = errorself.timeline = timeline}
}

DownloadResponse

这个也没什么好说的,直接上代码:

/// Used to store all data associated with a serialized response of a download request.
public struct DownloadResponse<Value> {/// The URL request sent to the server.public let request: URLRequest?/// The server's response to the URL request.public let response: HTTPURLResponse?/// The temporary destination URL of the data returned from the server.public let temporaryURL: URL?/// The final destination URL of the data returned from the server if it was moved.public let destinationURL: URL?/// The resume data generated if the request was cancelled.public let resumeData: Data?/// The result of response serialization.public let result: Result<Value>/// The timeline of the complete lifecycle of the request.public let timeline: Timeline/// Returns the associated value of the result if it is a success, `nil` otherwise.public var value: Value? { return result.value }/// Returns the associated error value if the result if it is a failure, `nil` otherwise.public var error: Error? { return result.error }var _metrics: AnyObject?/// Creates a `DownloadResponse` instance with the specified parameters derived from response serialization.////// - parameter request:        The URL request sent to the server./// - parameter response:       The server's response to the URL request./// - parameter temporaryURL:   The temporary destination URL of the data returned from the server./// - parameter destinationURL: The final destination URL of the data returned from the server if it was moved./// - parameter resumeData:     The resume data generated if the request was cancelled./// - parameter result:         The result of response serialization./// - parameter timeline:       The timeline of the complete lifecycle of the `Request`. Defaults to `Timeline()`.////// - returns: The new `DownloadResponse` instance.public init(request: URLRequest?,response: HTTPURLResponse?,temporaryURL: URL?,destinationURL: URL?,resumeData: Data?,result: Result<Value>,timeline: Timeline = Timeline()){self.request = requestself.response = responseself.temporaryURL = temporaryURLself.destinationURL = destinationURLself.resumeData = resumeDataself.result = resultself.timeline = timeline}
}

DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible

extension DownloadResponse: CustomStringConvertible, CustomDebugStringConvertible {/// The textual representation used when written to an output stream, which includes whether the result was a/// success or failure.public var description: String {return result.debugDescription}/// The debug textual representation used when written to an output stream, which includes the URL request, the URL/// response, the temporary and destination URLs, the resume data, the response serialization result and the/// timeline.public var debugDescription: String {var output: [String] = []output.append(request != nil ? "[Request]: \(request!.httpMethod ?? "GET") \(request!)" : "[Request]: nil")output.append(response != nil ? "[Response]: \(response!)" : "[Response]: nil")output.append("[TemporaryURL]: \(temporaryURL?.path ?? "nil")")output.append("[DestinationURL]: \(destinationURL?.path ?? "nil")")output.append("[ResumeData]: \(resumeData?.count ?? 0) bytes")output.append("[Result]: \(result.debugDescription)")output.append("[Timeline]: \(timeline.debugDescription)")return output.joined(separator: "\n")}
}

protocol Response

protocol Response {/// The task metrics containing the request / response statistics.var _metrics: AnyObject? { get set }mutating func add(_ metrics: AnyObject?)
}extension Response {mutating func add(_ metrics: AnyObject?) {#if !os(watchOS)guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }guard let metrics = metrics as? URLSessionTaskMetrics else { return }_metrics = metrics#endif}
}// MARK: -@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DefaultDataResponse: Response {
#if !os(watchOS)/// The task metrics containing the request / response statistics.public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DataResponse: Response {
#if !os(watchOS)/// The task metrics containing the request / response statistics.public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DefaultDownloadResponse: Response {
#if !os(watchOS)/// The task metrics containing the request / response statistics.public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension DownloadResponse: Response {
#if !os(watchOS)/// The task metrics containing the request / response statistics.public var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}

上边的协议中有一个属性和一个方法,如果在协议中实现了自身的方法,那么实现该协议的对象可以不用实现该协议中的方法。**在上边介绍的属性中 _metrics是来自该协议的属性。在上边的初始化方法中也没有_metrics这一项**

在swift中,当多个对象公用一个属性或者方法时,就可以考虑协议了。

在这里按照上边的用法,举个简单的例子。

public struct Person {public var name: Stringpublic var age: UIntvar _hobby: String?init(name: String, age: UInt) {self.name = nameself.age = age}
}var person = Person(name: "James", age: 30)
print(person.name)person.name = "Bond"
print(person.name)var person1 = person
print(person1.name)person1.name = "Rose"
print(person1.name)
print(person.name)protocol Hobbyable {var _hobby: String? { get set }mutating func addHobby(_ hobby: String?)
}extension Hobbyable {mutating func addHobby(_ hobby: String?) {_hobby = hobby}
}extension Person: Hobbyable {var hobby: String? {return _hobby}
}person1.addHobby("Books")
print(person1.hobby ?? "")

总结

由于知识水平有限,如有错误,还望指出

链接

Alamofire源码解读系列(一)之概述和使用 简书-----博客园

Alamofire源码解读系列(二)之错误处理(AFError) 简书-----博客园

Alamofire源码解读系列(三)之通知处理(Notification) 简书-----博客园

Alamofire源码解读系列(四)之参数编码(ParameterEncoding) 简书-----博客园

Alamofire源码解读系列(五)之结果封装(Result) 简书-----博客园

Alamofire源码解读系列(六)之Task代理(TaskDelegate) 简书-----博客园

Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 简书-----博客园

Alamofire源码解读系列(八)之安全策略(ServerTrustPolicy) 简书-----博客园

Alamofire源码解读系列(九)之响应封装(Response)相关推荐

  1. Alamofire源码解读系列(五)之结果封装(Result)

    本篇讲解Result的封装 前言 有时候,我们会根据现实中的事物来对程序中的某个业务关系进行抽象,这句话很难理解.在Alamofire中,使用Response来描述请求后的结果.我们都知道Alamof ...

  2. Alamofire源码解读系列(十二)之请求(Request)

    本篇是Alamofire中的请求抽象层的讲解 前言 在Alamofire中,围绕着Request,设计了很多额外的特性,这也恰恰表明,Request是所有请求的基础部分和发起点.这无疑给我们一个Req ...

  3. Alamofire源码解读系列(十一)之多表单(MultipartFormData)

    本篇讲解跟上传数据相关的多表单 前言 我相信应该有不少的开发者不明白多表单是怎么一回事,然而事实上,多表单确实很简单.试想一下,如果有多个不同类型的文件(png/txt/mp3/pdf等等)需要上传给 ...

  4. Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager)

    Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 本篇主要讲解iOS开发中的网络监控 前言 在开发中,有时候我们需要获取这些信息: 手机是否联网 ...

  5. Alamofire源码解读系列(一)之概述和使用

    尽管Alamofire的github文档已经做了很详细的说明,我还是想重新梳理一遍它的各种用法,以及这些方法的一些设计思想 前言 因为之前写过一个AFNetworking的源码解读,所以就已经比较了解 ...

  6. spring源码解读系列(八):观察者模式--spring监听器详解

    一.前言 在前面的文章spring源码解读系列(七)中,我们继续剖析了spring的核心refresh()方法中的registerBeanPostProcessors(beanFactory)(完成B ...

  7. py-faster-rcnn源码解读系列

    转载自: py-faster-rcnn源码解读系列(一)--train_faster_rcnn_alt_opt.py - sunyiyou9的博客 - 博客频道 - CSDN.NET http://b ...

  8. Hadoop源码解读系列目录

    Hadoop源码解读系列 1.hadoop源码|common模块-configuration详解 2.hadoop源码|core模块-序列化与压缩详解 3.hadoop源码|core模块-远程调用与N ...

  9. 【注意力机制集锦】Channel Attention通道注意力网络结构、源码解读系列一

    Channel Attention网络结构.源码解读系列一 SE-Net.SK-Net与CBAM 1 SENet 原文链接:SENet原文 源码链接:SENet源码 Squeeze-and-Excit ...

最新文章

  1. 8.使用hydra对端口进行爆破
  2. 两大主流Web服务器之分析与对比
  3. 使用 frida+dexdump对apk脱壳
  4. KOFLive Beta Daily-Scrum 9
  5. Andriod Atom x86模拟器启动报错
  6. android 日历 课程设计,课程设计-数字日历的设计
  7. 饿了别叫妈,叫阿里“爸爸”!
  8. 爬虫 selenium
  9. 手眼标定,我的结果显示手和眼相距上千米!手眼标定结果准确率如何提高?
  10. 可能是最全的Kotlin协程讲解
  11. 传递给Appium服务器以开启相应安卓Automation会话的Capabilities的几点说明
  12. 3d打印材料有哪几种
  13. 启动tomcat报错:Could not load the Tomcat server configuration at \Servers\Tomcat v7.0 Server at localhos
  14. 348高校毕业设计选题
  15. 跨站请求伪造(CSRF)示例、原理及其防御措施
  16. “bang” in JavaScript
  17. 物理渲染数学(s2013_pbs_physics_math_notes)
  18. 平台型OA定制开发的优势所在
  19. 对九类客户的心理诊断
  20. oracle crs 不能启动,CRS无法启动的解决过程

热门文章

  1. c++ primer,友元函数上的一个例子(By Sybase)
  2. SQL SERVER 2008 创建,删除,添加表的主键
  3. js 、jq强化复习
  4. linux 下 `dirname $0` 脚本文件放置的目录
  5. 简明现代魔法博客图书馆之php学习记录
  6. 在Linux下安装RabbitMQ
  7. blog摘录--测试感触
  8. 参加中国十大IT杰出博客
  9. Maven : 将Jar安装到本地仓库和Jar上传到私服[转]
  10. lLinux网络相关命令,防火墙介绍及相关命令