AFNetworking是iOS界知名的网络三方库,现已完全取代了ASI。最新的AFNetworking3.0也早已从NSURLConnection切换到了NSURLSession,使用起来也更加方便。作为一名不断探索的资深iOSer,还是要看看源码提升下内功。

一、概述

首先看下AFNetworking的结构及其继承关系:

ClassSuperClassDescriptionAFURLSessionManagerNSObject①用于管理NSURLSession实例。②负责生成dataTask、uploadTask和downloadTask。AFHTTPSessionManagerAFURLSessionManagerAFURLSessionManager的子类,封装了网络请求并提供了Convenience Methods发起HTTP请求。AFHTTPRequestSerializerNSObject生成网络请求所需的Request,包括对参数的处理。AFHTTPResponseSerializerNSObject解析返回来的Response,并验证合法性。AFSecurityPolicyNSObject主要处理HTTPs通信。AFURLSessionManagerTaskDelegateNSObject作为task的delegate,调用回调。

涉及的主要类都在上表中了,下面简单说下其他的辅助类:

(1)_AFURLSessionTaskSwizzling:这个类只做一件事:使用Method Swizzling更改NSURLSessionDataTask及其父类的resumesuspend实现,在其调用时发送消息:AFNSURLSessionTaskDidResumeNotificationAFNSURLSessionTaskDidSuspendNotification即:

- (void)af_resume { NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); NSURLSessionTaskState state = [self state]; [self af_resume]; if (state != NSURLSessionTaskStateRunning) { [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self]; }}- (void)af_suspend { NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state"); NSURLSessionTaskState state = [self state]; [self af_suspend]; if (state != NSURLSessionTaskStateSuspended) { [[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self]; }}

(2)AFJSONRequestSerializer:是AFHTTPRequestSerializer的子类,相比于AFHTTPRequestSerializer,它增加了对parameters是否是合法JSON格式的校验。在POST情况下,parameters会通过NSJSONSerialization转化为NSData放到HTTPBody里。此外,header的Content-Type也会被设置为application/json

(3)AFQueryStringPair:包含fieldvalue属性,用于表示参数(eg. name='layne'),并且field和value要经过“PercentEscaped”处理,处理函数如下:

NSString * AFPercentEscapedStringFromString(NSString *string) { static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4 static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;="; NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy]; [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]]; // FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028 // return [string stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; static NSUInteger const batchSize = 50; NSUInteger index = 0; NSMutableString *escaped = @"".mutableCopy; while (index < string.length) { NSUInteger length = MIN(string.length - index, batchSize); NSRange range = NSMakeRange(index, length); // To avoid breaking up character sequences such as emoji range = [string rangeOfComposedCharacterSequencesForRange:range]; NSString *substring = [string substringWithRange:range]; NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet]; [escaped appendString:encoded]; index += range.length; } return escaped;}

这里有两点需要说明:

①对于字符截断问题(eg.emoji),这里使用了:rangeOfComposedCharacterSequencesForRange:,根据给定的range调整实际的range来防止字符截断。

②这里设置了batchSize分块进行escape。为啥要这么做?FIXME: https://github.com/AFNetworking/AFNetworking/pull/3028给出了具体解释:

Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead.

简单说就是,在8.1和8.2上超过100个中文字符会挂。

(4)AFStreamingMultipartFormData用于multipart方式上传的formData.

(5)AFHTTPBodyPart

(6)AFMultipartBodyStream

(7)AFJSONResponseSerializerAFHTTPResponseSerializer的子类,解析JSON格式的response.

(8)AFXMLParserResponseSerializerAFHTTPResponseSerializer的子类,解析(NSXMLParser)XML格式的response.

(9)AFXMLDocumentResponseSerializerAFHTTPResponseSerializer的子类,解析(NSXMLDocument)XML格式的response.

(10)AFPropertyListResponseSerializerAFHTTPResponseSerializer的子类,解析(NSXMLDocument)PropertyList格式的response,

(11)AFImageResponseSerializerAFHTTPResponseSerializer的子类,解析图片response。

(12)AFCompoundResponseSerializerAFHTTPResponseSerializer的子类,解析复合类型的response。

二、类分析1.AFURLSessionManager

AFURLSessionManager是管理网络请求的主类,它的结构如下:

管理着

一个session(NSURLSession实例),用于发起网络请求。

一个operationQueue,用于执行代理回调。

一个responseSerializer(实现了AFURLResponseSerialization),用于response解析。

一个securityPolicy(AFSecurityPolicy实例),用于HTTPs配置。

一个reachabilityManager(AFNetworkReachabilityManager实例),用于网络连通性监听。

通过重写tasksdataTasksuploadTasksdownloadTasks属性的getter方法,使用getTasksWithCompletionHandler:获取session管理的tasks。

提供多种生成task的函数。如:

-dataTaskWithRequest:completionHandler:-dataTaskWithRequest:uploadProgress:downloadProgress:completionHandler:-uploadTaskWithRequest:fromFile:progress:completionHandler:-uploadTaskWithRequest:fromData:progress:completionHandler:-uploadTaskWithStreamedRequest:progress:completionHandler:-downloadTaskWithRequest:progress:destination:completionHandler:-downloadTaskWithResumeData:progress:destination:completionHandler:

监控上传/下载进度。

-uploadProgressForTask: -downloadProgressForTask:

定义回调block属性,每个block对应NSURLSession相关的delegate方法。

@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;#if AF_CAN_INCLUDE_SESSION_TASK_METRICS@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics;#endif@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;

声明了常量。

//通知AFNetworkingTaskDidResumeNotificationAFNetworkingTaskDidCompleteNotificationAFNetworkingTaskDidSuspendNotificationAFURLSessionDidInvalidateNotificationAFURLSessionDownloadTaskDidFailToMoveFileNotification//通知AFNetworkingTaskDidCompleteNotification中userInfo的keyAFNetworkingTaskDidCompleteResponseDataKeyAFNetworkingTaskDidCompleteSerializedResponseKeyAFNetworkingTaskDidCompleteResponseSerializerKeyAFNetworkingTaskDidCompleteAssetPathKeyAFNetworkingTaskDidCompleteErrorKey

在生成task时为每个task生成对应的delegate(AFURLSessionManagerTaskDelegate实例),并使用{<taskID:delegate>}的形式保存在可变字典mutableTaskDelegatesKeyedByTaskIdentifier中。

作为NSURLSession的delegate,实现的delegate方法有:

/* ----------NSURLSessionDelegate---------- *///执行sessionDidBecomeInvalid block并发通知- (void)URLSession:didBecomeInvalidWithError://生成disposition(NSURLSessionAuthChallengeDisposition实例),并调用completionHandler- (void)URLSession:didReceiveChallenge:completionHandler://执行didFinishEventsForBackgroundURLSession block- (void)URLSessionDidFinishEventsForBackgroundURLSession:/* ----------NSURLSessionTaskDelegate---------- *///执行taskWillPerformHTTPRedirectionBlock生成新的request,并调用completionHandler- (void)URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler://生成disposition(NSURLSessionAuthChallengeDisposition实例),并调用completionHandler- (void)URLSession:task:didReceiveChallenge:completionHandler://生成inputStream(NSInputStream实例),并调用completionHandler- (void)URLSession:task:needNewBodyStream://转到task delegate中执行,并执行taskDidSendBodyData block- (void)URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend://转到task delegate中执行,并执行taskDidComplete block- (void)URLSession:task:didCompleteWithError:/* ----------NSURLSessionDataDelegate---------- *///执行dataTaskDidReceiveResponse block生成disposition,并调用completionHandler- (void)URLSession:dataTask:didReceiveResponse:completionHandler://重新设置task delegate,并调用dataTaskDidBecomeDownloadTask block- (void)URLSession:dataTask:didBecomeDownloadTask://转到task delegate中执行,并调用dataTaskDidReceiveData block- (void)URLSession:dataTask:didReceiveData://执行dataTaskWillCacheResponse block生成cacheResponse,并调用completionHandler- (void)URLSession:dataTask:willCacheResponse:completionHandler:/* ----------NSURLSessionDownloadDelegate---------- *///转到task delegate中执行,并移动文件- (void)URLSession:downloadTask:didFinishDownloadingToURL://转到task delegate中执行,并执行downloadTaskDidWriteData block- (void)URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite://转到task delegate中执行,并执行downloadTaskDidResume block- (void)URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:2.AFURLSessionManagerTaskDelegate

AFURLSessionManagerTaskDelegate这个类虽然后缀是·-Delegate,但它并不是一个协议,而是一个继承自NSObject的类。它和AFURLSessionManager都定义在文件AFURLSessionManager.m中。它的实例作为task的代理使用。

包含一个manager属性,使用weak回指使用它的AFURLSessionManager实例。

包含控制上传和下载进度的属性uploadProgressdownloadProgress(均为NSProgress实例),通过KVO监测各自的fractionCompleted,从而在结束时调用downloadProgressBlockuploadProgressBlock

实现的delegate包括:

/* ----------NSURLSessionTaskDelegate---------- *///构造userInfo,并使用manager的responseSerializer解析response,最后调用self.completionHandler.- (void)URLSession:task:didCompleteWithError://更新uploadProgress属性- (void)URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:/* ----------NSURLSessionDataTask---------- *///更新downloadProgress属性,并用mutableData保存接收到的数据- (void)URLSession:dataTask:didReceiveData:/* ----------NSURLSessionDownloadTask-------- *///更新downloadProgress属性- (void)URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite://更新downloadProgress属性- (void)URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes://清空downloadFileURL(nil),并移动文件- (void)URLSession:downloadTask:didFinishDownloadingToURL:

注:AFURLSessionManagerTaskDelegate实例本身不持有task,它们之间的代理关系是以{<taskID:delegate>}的形式保存在可变字典mutableTaskDelegatesKeyedByTaskIdentifier中的。

3.AFHTTPSessionManager

AFHTTPSessionManagerAFURLSessionManager的子类,它针对HTTP请求封装了更为便利的方法。它的结构如下:

主要包含requestSerializer(AFHTTPRequestSerializer实例)和responseSerializer(AFHTTPResponseSerializer实例),分别用于request的封装及response的解析。

提供三个实例初始化方法:

+ (instancetype)manager;- (instancetype)initWithBaseURL:(nullable NSURL *)url;- (instancetype)initWithBaseURL:(nullable NSURL *)url sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration;

最终调用的都是第三个函数。

封装的Convenience Method如下:

GET

- GET:parameters:headers:progress:success:failure:POST

- POST:parameters:headers:progress:success:failure:- POST:paramters:headers:constructingBodyWithBlock:progress:success:failure:HEAD

- HEAD:parameters:headers:success:failure:PUT

- PUT:parameters:headers:success:failure:PATCH

- PATCH:parameters:headers:success:failure:DELETE

- DELETE:paramaters:headers:success:failure:

注:上面只列出了有效的方法,其他的都已经被标记为DEPRECATED_ATTRIBUTE了。

除了包含...constructingBodyWithBlock…的POST函数外,其余的convenience methods都是通过以下函数生成对应的dataTask:

- (NSURLSessionDataTask *)dataTaskWithHTTPMethod: URLString: parameters: uploadProgress: downloadProgress: success: failure:

在上述函数中,requestSerializer会通过HTTPMethod、URLString和parameters生成request,然后会调用父类的:dataTaskWithRequest:uploadProgress:downloadProgress: completionHandler:生成dataTask并返回。返回的dataTask会被resume启动。

4.AFHTTPRequestSerializer

AFHTTPRequestSerializer继承自NSObject,用于封装request。

实现了协议AFURLRequestSerialization。这个协议只有一个函数:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error;

用于将参数包含到原始request中形成新的request。

使用数组mutableHTTPRequestHeaders保存要包含在request header中的数据。默认包含Accept-LanguageUser-Agent。其中,在设置User-Agent时,为了保证ASCII编码规则,作者使用了ICU文本变换。

CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)

ICU 库提供了一整套强大的文本变换功能,可以实现不用语系之间的字符转换,如汉字转拼音。在上面的例子中,User-Agent字段会先被转换为Latin,接着变换为Latin-ASCII,最后清除所有不是ASCII的字符。 其他的变换可参考 ICU 用户手册。

采用KVO机制监测相关属性,若用户设置了对应属性,该属性会被记录下来,在生成request时加入。

allowsCellularAccesscachePolicyHTTPShouldHandleCookiesHTTPShouldUsePipeliningnetworkServiceTypetimeoutInterval首先重写+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key,禁止以上6个字段的KVO自动触发。在以上6个字段的setter中使用willChangeValueForKeydidChangeValueForKey手动触发KVO。

生成request使用以下函数:

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *__autoreleasing *)error

执行的操作包括:

① 根据URLString和method创建mutableRequest

NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];mutableRequest.HTTPMethod = method;

② 使用KVC将KVO监测的6个字段(用户设置过的)包含进mutableRequest中。

for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) { if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) { [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath]; }}

③ 调用AFURLRequestSerialization协议方法- requestBySerializingRequest: withParameters: error:。在这个协议方法内部执行:

设置request的header将parameters格式化。默认的格式化形如name=layne$age=30&job=engineer。根据请求的Method(GET、POST、HEAD等)不同将参数加到request的不同位置(URL or Body)。5.AFJSONRequestSerializer

AFJSONRequestSerializerAFHTTPRequestSerializer的子类,使用NSJSONSerialization将参数编码成JSON格式,并设置Content-Typeapplication/json。它重写了AFURLRequestSerialization协议方法:

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *__autoreleasing *)error{ NSParameterAssert(request); if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) { return [super requestBySerializingRequest:request withParameters:parameters error:error]; }//若为GET/HEAD/DELETE方法,由于参数都拼接在URL中,因此无所谓json不json,直接调用父类的方法即可。 NSMutableURLRequest *mutableRequest = [request mutableCopy]; [self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { if (![request valueForHTTPHeaderField:field]) { [mutableRequest setValue:value forHTTPHeaderField:field]; } }];//更新header数据 if (parameters) { if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) { [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; }//设置Content-Type字段为“application/json” if (![NSJSONSerialization isValidJSONObject:parameters]) { if (error) { NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"The `parameters` argument is not valid JSON.", @"AFNetworking", nil)}; *error = [[NSError alloc] initWithDomain:AFURLRequestSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo]; } return nil; }//非法的json格式(NSDictionary)数据 NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error];//json序列化 if (!jsonData) { return nil; } [mutableRequest setHTTPBody:jsonData]; } return mutableRequest;}6.AFHTTPResponseSerializer

AFHTTPResonseSerializer继承自NSObject,实现了AFURLResponseSerialization协议:

- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response data:(nullable NSData *)data error:(NSError * _Nullable __autoreleasing *)error;

在协议方法中根据acceptableStatusCodesacceptableContentTypes判断response合法性。

7.AFJSONResponseSerializer

AFJSONResponseSerializerAFHTTPResponseSerializer的子类。

设置acceptableContentTypes指定合法的content-type:

application/jsontext/jsontext/javascript

重写AFURLResponseSerialization协议方法:

- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response data:(nullable NSData *)data error:(NSError * _Nullable __autoreleasing *)error;

在内部:

(1)根据acceptableStatusCodesacceptableContentTypes判断response合法性;

(2)使用NSJSONSerialization将data转换为NSDictionary

(3)根据removesKeysWithNullValues的值决定是否将NSDictionary中NSNull的数据清除。

以上就是AFNetworking主要的类结构及其功能。下一篇博客我们会以一个简单的POST请求来走一遍逻辑,看看AFN到底是如何工作的。