AFNetworking是iOS界知名的网络三方库,现已完全取代了ASI。最新的AFNetworking3.0也早已从NSURLConnection切换到了NSURLSession,使用起来也更加方便。作为一名不断探索的资深iOSer,还是要看看源码提升下内功。
首先看下AFNetworking的结构及其继承关系:
Class | SuperClass | Description |
---|---|---|
AFURLSessionManager |
NSObject |
①用于管理NSURLSession实例。②负责生成dataTask、uploadTask和downloadTask。 |
AFHTTPSessionManager |
AFURLSessionManager |
AFURLSessionManager的子类,封装了网络请求并提供了Convenience Methods发起HTTP请求。 |
AFHTTPRequestSerializer |
NSObject |
生成网络请求所需的Request,包括对参数的处理。 |
AFHTTPResponseSerializer |
NSObject |
解析返回来的Response,并验证合法性。 |
AFSecurityPolicy |
NSObject |
主要处理HTTPs通信。 |
AFURLSessionManagerTaskDelegate |
NSObject |
作为task的delegate,调用回调。 |
涉及的主要类都在上表中了,下面简单说下其他的辅助类:
(1)_AFURLSessionTaskSwizzling
:这个类只做一件事:使用Method Swizzling更改NSURLSessionDataTask及其父类的resume
及suspend
实现,在其调用时发送消息:AFNSURLSessionTaskDidResumeNotification
和AFNSURLSessionTaskDidSuspendNotification
即:
- (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
:包含field
和value
属性,用于表示参数(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)AFJSONResponseSerializer
:AFHTTPResponseSerializer
的子类,解析JSON格式的response.
(8)AFXMLParserResponseSerializer
:AFHTTPResponseSerializer
的子类,解析(NSXMLParser
)XML格式的response.
(9)AFXMLDocumentResponseSerializer
:AFHTTPResponseSerializer
的子类,解析(NSXMLDocument
)XML格式的response.
(10)AFPropertyListResponseSerializer
:AFHTTPResponseSerializer
的子类,解析(NSXMLDocument
)PropertyList格式的response,
(11)AFImageResponseSerializer
:AFHTTPResponseSerializer
的子类,解析图片response。
(12)AFCompoundResponseSerializer
:AFHTTPResponseSerializer
的子类,解析复合类型的response。
AFURLSessionManager
是管理网络请求的主类,它的结构如下:
管理着
一个session
(NSURLSession实例),用于发起网络请求。
一个operationQueue
,用于执行代理回调。
一个responseSerializer
(实现了AFURLResponseSerialization
),用于response解析。
一个securityPolicy
(AFSecurityPolicy
实例),用于HTTPs配置。
一个reachabilityManager
(AFNetworkReachabilityManager
实例),用于网络连通性监听。
通过重写tasks
、dataTasks
、uploadTasks
和downloadTasks
属性的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;
声明了常量。
//通知
AFNetworkingTaskDidResumeNotification
AFNetworkingTaskDidCompleteNotification
AFNetworkingTaskDidSuspendNotification
AFURLSessionDidInvalidateNotification
AFURLSessionDownloadTaskDidFailToMoveFileNotification
//通知AFNetworkingTaskDidCompleteNotification中userInfo的key
AFNetworkingTaskDidCompleteResponseDataKey
AFNetworkingTaskDidCompleteSerializedResponseKey
AFNetworkingTaskDidCompleteResponseSerializerKey
AFNetworkingTaskDidCompleteAssetPathKey
AFNetworkingTaskDidCompleteErrorKey
在生成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:
AFURLSessionManagerTaskDelegate
这个类虽然后缀是·-Delegate
,但它并不是一个协议,而是一个继承自NSObject
的类。它和AFURLSessionManager
都定义在文件AFURLSessionManager.m
中。它的实例作为task的代理使用。
包含一个manager
属性,使用weak
回指使用它的AFURLSessionManager
实例。
包含控制上传和下载进度的属性uploadProgress
和downloadProgress
(均为NSProgress
实例),通过KVO监测各自的fractionCompleted
,从而在结束时调用downloadProgressBlock
和uploadProgressBlock
。
实现的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
中的。
AFHTTPSessionManager
是AFURLSessionManager
的子类,它针对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:parameters:headers:progress:success:failure:
- POST:parameters:headers:progress:success:failure:
- POST:paramters:headers:constructingBodyWithBlock:progress:success:failure:
- HEAD:parameters:headers:success:failure:
- PUT:parameters:headers:success:failure:
- PATCH:parameters:headers:success:failure:
- 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启动。
AFHTTPRequestSerializer
继承自NSObject
,用于封装request。
实现了协议AFURLRequestSerialization
。这个协议只有一个函数:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error;
用于将参数包含到原始request中形成新的request。
使用数组mutableHTTPRequestHeaders
保存要包含在request header中的数据。默认包含Accept-Language
和User-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时加入。
allowsCellularAccess
cachePolicy
HTTPShouldHandleCookies
HTTPShouldUsePipelining
networkServiceType
timeoutInterval
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
,禁止以上6个字段的KVO自动触发。willChangeValueForKey
和didChangeValueForKey
手动触发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:
。在这个协议方法内部执行:
name=layne$age=30&job=engineer
。AFJSONRequestSerializer
是AFHTTPRequestSerializer
的子类,使用NSJSONSerialization
将参数编码成JSON格式,并设置Content-Type
为application/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;
}
AFHTTPResonseSerializer
继承自NSObject
,实现了AFURLResponseSerialization
协议:
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error;
在协议方法中根据acceptableStatusCodes
和acceptableContentTypes
判断response合法性。
AFJSONResponseSerializer
是AFHTTPResponseSerializer
的子类。
设置acceptableContentTypes
指定合法的content-type:
application/json
text/json
text/javascript
重写AFURLResponseSerialization
协议方法:
- (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error;
在内部:
(1)根据acceptableStatusCodes
和acceptableContentTypes
判断response合法性;
(2)使用NSJSONSerialization
将data转换为NSDictionary
(3)根据removesKeysWithNullValues
的值决定是否将NSDictionary中NSNull的数据清除。
以上就是AFNetworking主要的类结构及其功能。下一篇博客我们会以一个简单的POST请求来走一遍逻辑,看看AFN到底是如何工作的。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。