Alamofire源码解析专栏文章地址
上篇文章中我们讲到了Session是Alamofire整个系统中的核心类,主要负责请求的创建,分发,管理等,其内部的大部分逻辑都是在与Request类交互,Request类是Alamofire系统中用于代表一个请求的类,在想要仔细阅读Session类的源码之前,我们需要先对Request类有一个了解,所以这一篇文章主要分析Request类以及与其相关的一些类的源码
Request代理-RequestDelegate Session是管理所有Request的类,Request类本身需要通过代理的方式与Session进行通信,以完成Session对Request的控制,Request代理又RequestDelegate协议定义
1 2 3 4 5 6 7 8 9 10 11 12 public protocol RequestDelegate : AnyObject { var sessionConfiguration: URLSessionConfiguration { get } var startImmediately: Bool { get } func cleanup (after request: Request) func retryResult (for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void ) func retryRequest (_ request: Request, withDelay timeDelay: TimeInterval?) }
看完了协议定义,我们来看一下在Session类中具体的协议实现,这个样子我们才能更清楚的了解Request与Session之间交流的方式与内容
1 2 3 4 5 6 7 8 9 10 11 12 13 extension Session : RequestDelegate { public var sessionConfiguration: URLSessionConfiguration { session.configuration } public var startImmediately: Bool { startRequestsImmediately } public func cleanup (after request: Request) { activeRequests.remove(request) } }
直接返回自身所拥有的URLSession的configuration属性
直接返回Sessoni自身的startRequestsImmediately属性
request结束之后,Session所做的仅仅是在自己的一个存储活跃Request的集合中将其删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 extension Session { func retrier (for request: Request) -> RequestRetrier? { if let requestInterceptor = request.interceptor, let sessionInterceptor = interceptor { return Interceptor (retriers: [requestInterceptor, sessionInterceptor]) } else { return request.interceptor ?? interceptor } } public func retryResult (for request: Request, dueTo error: AFError, completion: @escaping (RetryResult) -> Void ) { guard let retrier = retrier(for : request) else { rootQueue.async { completion(.doNotRetry) } return } retrier.retry(request, for : self , dueTo: error) { retryResult in self .rootQueue.async { guard let retryResultError = retryResult.error else { completion(retryResult); return } let retryError = AFError .requestRetryFailed(retryError: retryResultError, originalError: error) completion(.doNotRetryWithError(retryError)) } } } public func retryRequest (_ request: Request, withDelay timeDelay: TimeInterval?) { rootQueue.async { let retry: () -> Void = { guard !request.isCancelled else { return } request.prepareForRetry() self .perform(request) } if let retryDelay = timeDelay { self .rootQueue.after(retryDelay) { retry() } } else { retry() } } } }
retrier是Session类内部提供的一个帮助方法,该方法返回某个request对应的Retrier,这里说的Retrier就是之前在第一篇提到过的遵循RequestRetrier协议的结构,方法返回的retrier主要由Session全局的Interceptor与Request特有的Interceptor拦截器生成。如果全局与Request都有一个自己的拦截器,那么就会将这两个拦截器中的retry方法与adapt融合生成一个新的拦截器并按照RequestRetrier协议类型返回
该方法由Request出错之后主动调用,来获取重试结果
首先调用retrier帮助方法,获得一个retrier,如果获取到的retrier为空,则就放弃重试。
然后调用retrier的retry方法获取到retryResult(是否重试),然后再将这个结果通过Request调用时传进来的completion回调通知给Request
Request接收到是否重试的结果之后调用retryRequest方法让Session重新发起Request
从Reqeust那里接受一个延时的参数,延时重试Reqeust
重试Request的逻辑,首先是调用了request的prepareForRetry方法,让Request做出一些重试前的工作(比如重置状态,自增重试次数等),之后Session类的perform方法,这个方法是Session类中的核心方法,用于启动一个Request,之后我们会讲到
Request Alamofire中的Request模仿了系统中URLSessionTask的结构,存在着许多种不同的请求类,他们都以Request为基类。其中DataRequest、DownloadReqeust、DataStreamRequest都直接继承自基类Request,然后UploadRequest又继承自DataRequest。我们首先来看Request基类中定义了什么东西
State 首先是每个Request都拥有的状态,也是模仿的系统中URLSessionTask的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public enum State { case initialized case resumed case suspended case cancelled case finished func canTransitionTo (_ state: State) -> Bool { switch (self , state) { case (.initialized, _ ): return true case (_ , .initialized), (.cancelled, _ ), (.finished, _ ): return false case (.resumed, .cancelled), (.suspended, .cancelled), (.resumed, .suspended), (.suspended, .resumed): return true case (.suspended, .suspended), (.resumed, .resumed): return false case (_ , .finished): return true } } }
MutableState Request中在存储与自己相关的状态信息时并不是选择将一个个的状态直接作为自己的属性存储,而是做了一个中间层,这个中间层就是MutableState,MutableState存储了Request的大部分状态信息,然后Request只需要将MutableState作为自己的属性即可。为什么非要费时费力地增加一个MutableState中间层呢?请继续看,后面我们会说明原因,现在我们先来看一下MutableState的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 struct MutableState { var state: State = .initialized var uploadProgressHandler: (handler: ProgressHandler , queue: DispatchQueue )? var downloadProgressHandler: (handler: ProgressHandler , queue: DispatchQueue )? var redirectHandler: RedirectHandler? var cachedResponseHandler: CachedResponseHandler? var cURLHandler: ((String ) -> Void )? var responseSerializers: [() -> Void ] = [] var responseSerializerCompletions: [() -> Void ] = [] var responseSerializerProcessingFinished = false var credential: URLCredential? var requests: [URLRequest ] = [] var tasks: [URLSessionTask ] = [] var metrics: [URLSessionTaskMetrics ] = [] var retryCount = 0 var error: AFError? var isFinishing = false }
根据Session中有多个不同的DispatchQueue属性我们可以推断出,Request是极有可能在多个队列中并行操作的,这样就会带来资源的并发访问安全问题,正常的做法是持有一个锁,在对资源进行操作的时候进行对应的加锁解锁操作,但是属性有这么多,如果在每次都需要在修改属性的时候都额外进行一次加解锁操作,会使程序变得非常复杂,同时编写也更加容易出错,不利于进一步扩展。所以这就是引入MutableState的原因,将所有具有并发访问安全问题的属性放入到这个MutableState中,然后再对这个MutableState属性进行统一的加解锁处理。Alamofire中使用了Swift语言的新特性,使用了Property Wrapper的特性来优雅的实现并发安全的属性访问问题。在Request类的源码中,MutableState的属性是这样定义的
1 2 @Protected fileprivate var mutableState = MutableState ()
上述的属性定义中,使用到了一个名称为Protected的属性包装器,这个属性包装器是Alamofire为需要并发安全访问的属性提供的,接下来我们来分析这个属性包装器的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @propertyWrapper @dynamicMemberLookup final class Protected <T > { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) private let lock = UnfairLock () #elseif os(Linux ) private let lock = MutexLock () #endif private var value: T init (_ value: T ) { self .value = value } var wrappedValue: T { get { lock.around { value } } set { lock.around { value = newValue } } } extension Lock { func around <T>(_ closure: () -> T ) -> T { lock(); defer { unlock() } return closure() } func around (_ closure: () -> Void ) { lock(); defer { unlock() } closure() } } var projectedValue: Protected <T > { self } init (wrappedValue: T ) { value = wrappedValue } func read <U>(_ closure: (T) -> U ) -> U { lock.around { closure(self .value) } } @discardableResult func write <U>(_ closure: (inout T) -> U ) -> U { lock.around { closure(&self .value) } } subscript <Property >(dynamicMember keyPath: WritableKeyPath <T , Property >) -> Property { get { lock.around { value[keyPath: keyPath] } } set { lock.around { value[keyPath: keyPath] = newValue } } } }
使用Swift的属性包装器特性来为类的属性提供并发安全的特性是一种非常优雅的做法,这个东西我们也可以尝试用在自己的项目中,在需要解决并发安全的地方引入这个属性包装器,会使我们的工程代码更加优雅一些
Request类关键属性与生命周期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ```swift public class Request { public let id: UUID public let underlyingQueue: DispatchQueue public let serializationQueue: DispatchQueue public let eventMonitor: EventMonitor? public let interceptor: RequestInterceptor? public private (set ) weak var delegate: RequestDelegate? @Protected fileprivate var mutableState = MutableState () @Protected fileprivate var validators: [() -> Void ] = [] }
上面介绍了Request类中的成员属性,下面来介绍Request类中的一些方法,最开始是Request类中比较重要的与生命周期相关的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 public class Request { @discardableResult public func resume () -> Self { $mutableState.write { mutableState in guard mutableState.state.canTransitionTo(.resumed) else { return } mutableState.state = .resumed underlyingQueue.async { self .didResume() } guard let task = mutableState.tasks.last, task.state != .completed else { return } task.resume() underlyingQueue.async { self .didResumeTask(task) } } return self } func cleanup () { delegate?.cleanup(after: self ) } @discardableResult public func suspend () -> Self { $mutableState.write { mutableState in guard mutableState.state.canTransitionTo(.suspended) else { return } mutableState.state = .suspended underlyingQueue.async { self .didSuspend() } guard let task = mutableState.tasks.last, task.state != .completed else { return } task.suspend() underlyingQueue.async { self .didSuspendTask(task) } } return self } @discardableResult public func cancel () -> Self { $mutableState.write { mutableState in guard mutableState.state.canTransitionTo(.cancelled) else { return } mutableState.state = .cancelled underlyingQueue.async { self .didCancel() } guard let task = mutableState.tasks.last, task.state != .completed else { underlyingQueue.async { self .finish() } return } task.resume() task.cancel() underlyingQueue.async { self .didCancelTask(task) } } return self } func finish (error: AFError? = nil ) { dispatchPrecondition(condition: .onQueue(underlyingQueue)) guard !mutableState.isFinishing else { return } mutableState.isFinishing = true if let error = error { self .error = error } processNextResponseSerializer() eventMonitor?.requestDidFinish(self ) } func retryOrFinish (error: AFError?) { dispatchPrecondition(condition: .onQueue(underlyingQueue)) guard let error = error, let delegate = delegate else { finish(); return } delegate.retryResult(for : self , dueTo: error) { retryResult in switch retryResult { case .doNotRetry: self .finish() case let .doNotRetryWithError(retryError): self .finish(error: retryError.asAFError(orFailWith: "Received retryError was not already AFError" )) case .retry, .retryWithDelay: delegate.retryRequest(self , withDelay: retryResult.delay) } } } func prepareForRetry () { dispatchPrecondition(condition: .onQueue(underlyingQueue)) $mutableState.write { $0 .retryCount += 1 } reset() eventMonitor?.requestIsRetrying(self ) } func reset () { error = nil uploadProgress.totalUnitCount = 0 uploadProgress.completedUnitCount = 0 downloadProgress.totalUnitCount = 0 downloadProgress.completedUnitCount = 0 $mutableState.write { state in state.isFinishing = false state.responseSerializerCompletions = [] } } }
Request类序列化任务处理流程 上述分析了一些与Request整个生命周期相关的方法代码,下面开始分析与Request序列化任务相关的代码。在开始之前我们要了解Request中的序列化任务是什么,在我们开发使用Alamofire是,经常使用AF.request(…).reponse(…)或者AF.request(…).reponseJson(…)这样子的链式调用来处理一个网络请求,最后面调用的response()与responseJson()方法就是在为当前请求的Request添加一个响应的序列化任务,序列化任务主要负责将网络返回的响应序列化成你像要的数据并在完成任务之后调用对应的回调。
Request类中对于序列化任务的处理逻辑十分复杂,我也是前前后后看了好多遍才理解其内部运行的机制,虽然代码量不少,但是逻辑非常的绕。我这里会将其内部大概的运行机制先描述一遍,希望能使你们在观看以下源码的时候理解起来可以更加轻松一些。当然,一时看不懂请先不要着急,看完一遍有个大概地印象之后,再细细品味每个方法的作用便可参透。
我们对Request调用response或者responseJSON等序列化方法,想要拿到请求之后的数据的时候,实际上就是在向Request中添加一个序列化任务,每一个序列化任务都对应一个序列化任务回调,该回调会在序列化任务结束的时候调用。我们用户通过response或者responseJSON方法传入进去的闭包大多数情况下就直接被作为这个序列化任务的回调,但是呢,Alamofire就在这里玩了一个骚操作,序列化任务与序列化回调并不会同时都被加入到Request中去,而是只添加一个序列化任务并通知Request开始处理未处理的序列化任务或者序列化闭包,然后在这个序列化任务的末尾(也就是真正的序列化都已经执行完毕,但这个序列化任务闭包还没结束的时候),将该序列化任务对应的回调添加进Request中去,同时也会去通知Request去处理未处理的序列化任务或者序列化闭包。
PS:这里的逻辑很绕,我的文本表达能力也不是很好,应该没有特别清晰地将这段流程表达清楚,大家可以看下面更具有逻辑性的描述。另外这里为什么要搞一套这么复杂的流程来处理序列化任务的问题,笔者目前还是不太清楚的,仅仅是了解了其基本流程(因为还存在其他更为直接的方法来处理序列化任务以及任务的回调),等我探索到原因之后一定火速更新到这里。
同时我还提供了两个在不同情况下,序列化任务的处理流程,你在阅读代码的时候,可以分别将这两种代码带入到源码中去,便阅读源码边验证下面我所书写的流程,应该会有不小的收获
当一个请求在同一时间只有一个序列化任务被添加的时候,执行的流程就是这个样子的:
序列化任务被加入到Request中
开始处理这个序列化任务(通过serializationQueue异步处理)
在序列化任务即将完成时,序列化任务对应的回调将会被加入到Request中
开始处理这个回调
回调处理完毕,清理Request的资源,并设置相关标识符,表示自己已经完成所有任务
当一个请求在同一时间被添加了两个序列化任务的时候,执行的流程是这个样子的:
序列化任务A被添加到Request中
开始处理序列化任务A(通过serializationQueue异步处理)
序列化任务B被添加到Request中
在序列化任务B被添加时,通过Request的一些状态标识发现该Request正在处理其他的序列化任务,就停止自己的下一步行为了
序列化任务A即将完成时,序列化任务A对应的回调将会被加入到Request中
Request发现此时还有未经过处理的序列化任务(任务B),然后就开始处理序列化任务B(通过serializationQueue异步处理)
序列化任务B即将完成时,序列化任务B对应的回调将会被加入到Request中
Request发现此时拥有的所有序列化任务都处理完了,然后就清除所有的序列化任务,执行先前被添加到Request中的所有回调(回调A,回调B),并清除自身拥有的所有回调。最后清理Request的资源,并设置相关标识符,表示自己已经完成所有任务
下面是源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 extension DataRequest { @discardableResult public func response (queue: DispatchQueue = .main, completionHandler: @escaping (AFDataResponse<Data?>) -> Void ) -> Self { appendResponseSerializer { let result = AFResult <Data? >(value: self .data, error: self .error) self .underlyingQueue.async { let response = DataResponse (request: self .request, response: self .response, data: self .data, metrics: self .metrics, serializationDuration: 0 , result: result) self .eventMonitor?.request(self , didParseResponse: response) self .responseSerializerDidComplete { queue.async { completionHandler(response) } } } } return self } } public class Request { func appendResponseSerializer (_ closure: @escaping () -> Void ) { $mutableState.write { mutableState in mutableState.responseSerializers.append(closure) if mutableState.state == .finished { mutableState.state = .resumed } if mutableState.responseSerializerProcessingFinished { underlyingQueue.async { self .processNextResponseSerializer() } } if mutableState.state.canTransitionTo(.resumed) { underlyingQueue.async { if self .delegate?.startImmediately == true { self .resume() } } } } } func responseSerializerDidComplete (completion: @escaping () -> Void ) { $mutableState.write { $0 .responseSerializerCompletions.append(completion) } processNextResponseSerializer() } func processNextResponseSerializer () { guard let responseSerializer = nextResponseSerializer() else { var completions: [() -> Void ] = [] $mutableState.write { mutableState in completions = mutableState.responseSerializerCompletions mutableState.responseSerializers.removeAll() mutableState.responseSerializerCompletions.removeAll() if mutableState.state.canTransitionTo(.finished) { mutableState.state = .finished } mutableState.responseSerializerProcessingFinished = true mutableState.isFinishing = false } completions.forEach { $0 () } cleanup() return } serializationQueue.async { responseSerializer() } } func nextResponseSerializer () -> (() -> Void )? { var responseSerializer: (() -> Void )? $mutableState.write { mutableState in let responseSerializerIndex = mutableState.responseSerializerCompletions.count if responseSerializerIndex < mutableState.responseSerializers.count { responseSerializer = mutableState.responseSerializers[responseSerializerIndex] } } return responseSerializer } }
Request类的其他函数 除了上面的那些重要并且逻辑比较难懂的方法之外,Request类中还有一系列简单易懂的函数,有些函数是负责将Request的一些状态变更作为通知发送给Alamofire的通知中心,比如下面的一些例子:
1 2 3 4 5 6 7 func didResumeTask (_ task: URLSessionTask) { dispatchPrecondition(condition: .onQueue(underlyingQueue)) eventMonitor?.request(self , didResumeTask: task) }
还有一些函数是对外提供API接口,使用户可以使用Request的特定功能的函数,比如下面一些例子:
1 2 3 4 5 6 7 8 9 @discardableResult public func downloadProgress (queue: DispatchQueue = .main, closure: @escaping ProgressHandler) -> Self { mutableState.downloadProgressHandler = (handler: closure, queue: queue) return self }
除此之外还有一些函数值得大家去注意,那就是SessionDelegate与Request进行交互的方法。但这些函数并不存在于Request类中,前面我们就提到过Alamofire中的请求是一个系统,并不是单单一个类,并且Request在请求系统中扮演的角色只是一个抽象基类,是不会被真正使用的,其类内部只是定义了一些Request所共有的关键属性与生命周期。Request的每个子类都有自己需要完成的任务,不同的任务也就需要与SessionDelegate有不同种类的交互。这里我们举一个DataRequest的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func didReceive (data: Data) { if self .data == nil { mutableData = data } else { $mutableData.write { $0 ?.append(data) } } updateDownloadProgress() }
至此本篇关于Request类源码阅读的也就先告一段落了,希望大家都能在源码阅读的道路上有自己的收获