Alamofire源码解读系列(十二)之时间轴(Timeline)
本篇带来Alamofire中关于Timeline的一些思路
前言
Timeline翻译后的意思是时间轴,可以表示一个事件从开始到结束的时间节点。时间轴的概念能够应用在很多地方,比如说微博的主页就是一个时间轴。
Alamofire中Timeline的代码很少,非常简单。因此本篇文章中,我们不会把重点放到代码的解读上,我们通过追踪Timeline的身影,来讲讲关于代码设计方面的东东。
为什么要设计Timeline
很简单,我需要知道一个请求过程中,每个关键时间点的值或者时间点与时间点之间的距离。这样的一个需求不仅能够用于程序的调试,而且能为别的设计提供必要的参数支持。
我们通过下边的代码进行打印:
print(response.timeline)
显示的结果是:
Timeline: { "Latency": 0.092 secs, "Request Duration": 0.092 secs, "Serialization Duration": 0.458 secs, "Total Duration": 0.551 secs }
上边的代码提供的信息有:
Latency: 0.092 secs延迟,它表示从请求开始到收到或者发送第一个字节的时间长度,这里把它理解成建立连接花费的时间Request Duration: 0.092 secs请求时间,它表示从请求开始到结束的时间长度。这里跟Latency: 0.092 secs都是0.092,原因是我用的POST请求Serialization Duration: 0.458 secs序列化用时,这里用了0.458秒,说明在当前的这个请求中,最耗时的操作是数据的序列化,因此,程序可以在这方面进行优化Total Duration: 0.551 secs总耗时 用序列化完成的时间点减去请求开始的时间点
print(response.timeline)之所以能够打印出上边这些信息,是因为它重写了CustomStringConvertible协议的var description: String。当然,如果要打印更详细的信息,可以重写CustomDebugStringConvertible的var debugDescription: String。我们通过代码打印出来:
print(response.timeline.debugDescription)
打印结果是:
Timeline: { "Request Start Time": 513055266.217, "Initial Response Time": 513055266.241, "Request Completed Time": 513055266.241, "Serialization Completed Time": 513055266.752, "Latency": 0.024 secs, "Request Duration": 0.024 secs, "Serialization Duration": 0.511 secs, "Total Duration": 0.535 secs }
如何设计Timeline
Alamofire中,不管Request请求成功还是失败都会返回response。因此Timeline只有跟response绑定才合理。所以应该把他设为response的一个属性,在之前的文章中,我们也详细的介绍了response,他是一个struct类型的数据存储属性,因此在初始化的时候给Timeline赋值。
这样我们就解决了取出Timeline的问题,那么Timeline又是如何赋值的呢?我们在Alamofire中追踪Timeline的身影,最终发现只有三个文件中出现了它的身影:
Response.swiftTimeline作为Response的一个属性,肯定会出现在这里Timeline.swift这是它自身的实现ResponseSerialization.swift在这个文件中,为Request做了一个扩展,代码如下:extension Request {
var timeline: Timeline {
let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime return Timeline(
requestStartTime: self.startTime ?? CFAbsoluteTimeGetCurrent(),
initialResponseTime: initialResponseTime,
requestCompletedTime: requestCompletedTime,
serializationCompletedTime: CFAbsoluteTimeGetCurrent()
)
}
}
这个扩展说明Timeline的值最终是通过getter方法获取的,获取的是计算后的值。
对于这样的设计,能够给我们一些启示,我们在设计某一个功能的时候,尽量保持这个功能不去污染其他的程序。我们应该避免这样的设计:创建一个对象后,在程序的很多地方给它的属性赋值
代码
只是把代码弄上来:
/// Responsible for computing the timing metrics for the complete lifecycle of a `Request`.
public struct Timeline {
/// The time the request was initialized.
public let requestStartTime: CFAbsoluteTime
/// The time the first bytes were received from or sent to the server.
public let initialResponseTime: CFAbsoluteTime
/// The time when the request was completed.
public let requestCompletedTime: CFAbsoluteTime
/// The time when the response serialization was completed.
public let serializationCompletedTime: CFAbsoluteTime
/// The time interval in seconds from the time the request started to the initial response from the server.
public let latency: TimeInterval
/// The time interval in seconds from the time the request started to the time the request completed.
public let requestDuration: TimeInterval
/// The time interval in seconds from the time the request completed to the time response serialization completed.
public let serializationDuration: TimeInterval
/// The time interval in seconds from the time the request started to the time response serialization completed.
public let totalDuration: TimeInterval
/// Creates a new `Timeline` instance with the specified request times.
///
/// - parameter requestStartTime: The time the request was initialized. Defaults to `0.0`.
/// - parameter initialResponseTime: The time the first bytes were received from or sent to the server.
/// Defaults to `0.0`.
/// - parameter requestCompletedTime: The time when the request was completed. Defaults to `0.0`.
/// - parameter serializationCompletedTime: The time when the response serialization was completed. Defaults
/// to `0.0`.
///
/// - returns: The new `Timeline` instance.
public init(
requestStartTime: CFAbsoluteTime = 0.0,
initialResponseTime: CFAbsoluteTime = 0.0,
requestCompletedTime: CFAbsoluteTime = 0.0,
serializationCompletedTime: CFAbsoluteTime = 0.0)
{
self.requestStartTime = requestStartTime
self.initialResponseTime = initialResponseTime
self.requestCompletedTime = requestCompletedTime
self.serializationCompletedTime = serializationCompletedTime
self.latency = initialResponseTime - requestStartTime
self.requestDuration = requestCompletedTime - requestStartTime
self.serializationDuration = serializationCompletedTime - requestCompletedTime
self.totalDuration = serializationCompletedTime - requestStartTime
}
}
// MARK: - CustomStringConvertible
extension Timeline: CustomStringConvertible {
/// The textual representation used when written to an output stream, which includes the latency, the request
/// duration and the total duration.
public var description: String {
let latency = String(format: "%.3f", self.latency)
let requestDuration = String(format: "%.3f", self.requestDuration)
let serializationDuration = String(format: "%.3f", self.serializationDuration)
let totalDuration = String(format: "%.3f", self.totalDuration)
// NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
// fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
let timings = [
"\"Latency\": " + latency + " secs",
"\"Request Duration\": " + requestDuration + " secs",
"\"Serialization Duration\": " + serializationDuration + " secs",
"\"Total Duration\": " + totalDuration + " secs"
]
return "Timeline: { " + timings.joined(separator: ", ") + " }"
}
}
/// 使用timeline 可以让我们很清楚的查看某个网络请求过程的耗时,可以借此分析服务器端是不是有问题,同时也可以简介的得出当前的网络情况
// MARK: - CustomDebugStringConvertible
extension Timeline: CustomDebugStringConvertible {
/// The textual representation used when written to an output stream, which includes the request start time, the
/// initial response time, the request completed time, the serialization completed time, the latency, the request
/// duration and the total duration.
public var debugDescription: String {
let requestStartTime = String(format: "%.3f", self.requestStartTime)
let initialResponseTime = String(format: "%.3f", self.initialResponseTime)
let requestCompletedTime = String(format: "%.3f", self.requestCompletedTime)
let serializationCompletedTime = String(format: "%.3f", self.serializationCompletedTime)
let latency = String(format: "%.3f", self.latency)
let requestDuration = String(format: "%.3f", self.requestDuration)
let serializationDuration = String(format: "%.3f", self.serializationDuration)
let totalDuration = String(format: "%.3f", self.totalDuration)
// NOTE: Had to move to string concatenation due to memory leak filed as rdar://26761490. Once memory leak is
// fixed, we should move back to string interpolation by reverting commit 7d4a43b1.
let timings = [
"\"Request Start Time\": " + requestStartTime,
"\"Initial Response Time\": " + initialResponseTime,
"\"Request Completed Time\": " + requestCompletedTime,
"\"Serialization Completed Time\": " + serializationCompletedTime,
"\"Latency\": " + latency + " secs",
"\"Request Duration\": " + requestDuration + " secs",
"\"Serialization Duration\": " + serializationDuration + " secs",
"\"Total Duration\": " + totalDuration + " secs"
]
return "Timeline: { " + timings.joined(separator: ", ") + " }"
}
}
总结
通过解读源码学到了很多,除了学到了一些平时不了解的技术外,最大的收获就是学会了从设计的角度去开发程序。也许若干年后,你依然会记得当初某个程序的设计思想。
有时间会写一个如何管理时间复杂度的文章。
由于知识水平有限,如有错误,还望指出
链接
Alamofire源码解读系列(一)之概述和使用 简书-----博客园
Alamofire源码解读系列(二)之错误处理(AFError) 简书-----博客园
Alamofire源码解读系列(三)之通知处理(Notification) 简书-----博客园
Alamofire源码解读系列(四)之参数编码(ParameterEncoding) 简书-----博客园
Alamofire源码解读系列(五)之结果封装(Result) 简书-----博客园
Alamofire源码解读系列(六)之Task代理(TaskDelegate) 简书-----博客园
Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 简书-----博客园
Alamofire源码解读系列(八)之安全策略(ServerTrustPolicy) 简书-----博客园
Alamofire源码解读系列(九)之响应封装(Response) 简书-----博客园
Alamofire源码解读系列(十)之序列化(ResponseSerialization) 简书-----博客园
Alamofire源码解读系列(十一)之多表单(MultipartFormData) 简书-----博客园
Alamofire源码解读系列(十二)之时间轴(Timeline)的更多相关文章
- Alamofire源码解读系列(十二)之请求(Request)
本篇是Alamofire中的请求抽象层的讲解 前言 在Alamofire中,围绕着Request,设计了很多额外的特性,这也恰恰表明,Request是所有请求的基础部分和发起点.这无疑给我们一个Req ...
- Alamofire源码解读系列(十)之序列化(ResponseSerialization)
本篇主要讲解Alamofire中如何把服务器返回的数据序列化 前言 和前边的文章不同, 在这一篇中,我想从程序的设计层次上解读ResponseSerialization这个文件.更直观的去探讨该功能是 ...
- Alamofire源码解读系列(十一)之多表单(MultipartFormData)
本篇讲解跟上传数据相关的多表单 前言 我相信应该有不少的开发者不明白多表单是怎么一回事,然而事实上,多表单确实很简单.试想一下,如果有多个不同类型的文件(png/txt/mp3/pdf等等)需要上传给 ...
- Alamofire源码解读系列(二)之错误处理(AFError)
本篇主要讲解Alamofire中错误的处理机制 前言 在开发中,往往最容易被忽略的内容就是对错误的处理.有经验的开发者,能够对自己写的每行代码负责,而且非常清楚自己写的代码在什么时候会出现异常,这样就 ...
- Alamofire源码解读系列(四)之参数编码(ParameterEncoding)
本篇讲解参数编码的内容 前言 我们在开发中发的每一个请求都是通过URLRequest来进行封装的,可以通过一个URL生成URLRequest.那么如果我有一个参数字典,这个参数字典又是如何从客户端传递 ...
- Alamofire源码解读系列(三)之通知处理(Notification)
本篇讲解swift中通知的用法 前言 通知作为传递事件和数据的载体,在使用中是不受限制的.由于忘记移除某个通知的监听,会造成很多潜在的问题,这些问题在测试中是很难被发现的.但这不是我们这篇文章探讨的主 ...
- Alamofire源码解读系列(五)之结果封装(Result)
本篇讲解Result的封装 前言 有时候,我们会根据现实中的事物来对程序中的某个业务关系进行抽象,这句话很难理解.在Alamofire中,使用Response来描述请求后的结果.我们都知道Alamof ...
- Alamofire源码解读系列(六)之Task代理(TaskDelegate)
本篇介绍Task代理(TaskDelegate.swift) 前言 我相信可能有80%的同学使用AFNetworking或者Alamofire处理网络事件,并且这两个框架都提供了丰富的功能,我也相信很 ...
- Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager)
Alamofire源码解读系列(七)之网络监控(NetworkReachabilityManager) 本篇主要讲解iOS开发中的网络监控 前言 在开发中,有时候我们需要获取这些信息: 手机是否联网 ...
随机推荐
- C语言陷阱:浮点运算
在Stack overflow上看到这样一个问题. 计算如下表达式的值: P=(1/2-3/4)*(5/6-7/8)*…*[n/(n-1) - (n+2)/(n+3)]. 程序如下: #include ...
- BZOJ 1085: [SCOI2005]骑士精神(A*算法)
第一次写A*算法(这就是A*?如果这就是A*的话,那不就只是搜索的一个优化了= =,不过h函数如果弄难一点真的有些难设计) 其实就是判断t+h(x)(t为当前步数,h(x)为达到当前状态的最小步数) ...
- Spring Mvc + Easyui中根据查询结果导出文件
项目是典型的SpringMvc + Easyui,需求是前台页面根据查询条件导出生成CSV文件. 基本流程是:前台页面通过表单提交的方式,请求后台,后台接受到查询参数,根据查询参数查询到数据集合,最后 ...
- WP8.1开发中关于如何显示.gif格式动态格式图片方法
这几天又遇到个问题,就是如何显示动态图片,本来以为和显示静态图片一样,谁知不行,在网上一查才知道WP8.1不支持.gif格式动态图片的显示: 后来又在MSDN论坛上查找,也有人问类似的问题,后来就大概 ...
- Windows内存管理简介:
1:连续的内存空间分配: (1)单一连续分配:只能单作业,单任务运行: 分为系统和用户区:用户区是指除了系统需外左右的内存,由于单用户,单任务,要不都被占用,要不全空 (2):固定空间分配:固定分 ...
- 智能打印SDK---官方博客
(开源准备:开源为百度云打包开源,GIT太慢,SVN没有免费的,TFS有免费的也慢的要命) 开源地址:http://pan.baidu.com/s/1miobLoO 电子商务越来越成熟,提供的服务越来 ...
- http服务搭建
http服务器搭建 主配置文件在 /etc/httpd/conf/httpd.conf 安装http yum install httpd -y 启动http服务器 systemctl start ...
- HttpWebRequest 模拟网站登录获取数据
此文档仅仅是一个BaseCode,已做后续查阅 项目使用IBM Platform Symphony分布式平台,所有业务处理都在这个分布式平台上计算,需求是获取这些计算机机群的运行状态,和每一个服务的的 ...
- 每天一个Linux命令(22)--find命令详解
find 一些常用参数的一些常用实例和一些具体用法和注意事项. 1.使用 name 选项: 文件名选项是 find 命令最常用的选项,要么单独使用该选项,要么和其他选项一起使用. 可以使用某种文件名模 ...
- 第24篇 js小知识和“坑”
前面说了说了js的相关知识,基本上除了语法外,把项目常用的知识做了一个梳理,现在说下js的其它方面的知识,这些知识不成体系,属于不理解对于一般开发没什么太多影响,但如果理解清楚,可以更好去开发. js ...