AFNetworking是一个有趣的、常用的、必备的框架,学一下不会吃亏;
AFURLRequestSerialization
AFURLRequestSerialization
协议的主要职责就是负责将原始请求拼装上参数并返回出来。
- (nullable NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(nullable id)parameters
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
框架提供了三个请求序列化器:
- AFHTTPRequestSerializer
提供查询字符串/ URL表单编码参数序列化(即是把参数拼接在URL后面)和默认请求头的具体基本实现,以及响应状态代码和内容类型验证。 - AFJSONRequestSerializer
AFHTTPRequestSerializer
的子类,在父类提供功能的基础上修改参数编码格式,它使用NSJSONSerialization
将参数编码为JSON,将编码请求的“Content-Type”设置为application/ JSON
; - AFPropertyListRequestSerializer
AFHTTPRequestSerializer
的子类,它使用NSPropertyListSerializer
将参数编码为JSON,将编码请求的Content-Type
设置为application/x-plist(即是xml)
。
AFHTTPRequestSerializer
AFHTTPRequestSerializer
的初始化通过代码可以得知干了以下两个事情;
- 设置共有请求头Accept-Language和User-Agent;
- 为
networkServiceType
、timeoutInterval
、cachePolicy
等属性添加KVO;在重写setter的时候触发willChange
和didChange
;当设置这些属性时会标记他们并且作用在该Serializer
后面的每个请求;
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error {
NSMutableURLRequest *mutableRequest = [request mutableCopy];
//request添加header
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil;
if (parameters) {
if (self.queryStringSerialization) {
//自定义序列化功能
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
//参数序列化
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
//使用POST且序列化器是AFHTTPRequestSerializer就是以application/x-www-form-urlencoded这种表单的形式提交
if (!query) {
query = @"";
}
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
在AFQueryStringFromParameters
这里面完成了参数序列化,帮我们转义了参数中的非法字符!$&'()*+,;=
等。
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error {
NSURL *url = [NSURL URLWithString:URLString];
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
//让每个request都能生效之前设置过的属性
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
上面这个方法做的工作就是拿到URLString和参数生成一个我们要请求的request;
当然我们可以自己弄个custom request serializer,比如业务需要对request的部分基础参数进行压缩放进http body内部,需要hook生成request过程等等;
AFJSONRequestSerializer
将参数用NSJSONSerialization
序列化成JSON格式的数据放进HTTPBody里面,置Content-Type
为application/json
;如果是默认HTTP方法(默认是GET
, HEAD
和 DELETE
)将采用URL编码;
文件上传
先了解一下HTTP文件上传原理,HTTP1.1中数据是以ASCII码传输的,当我们用POST提交Form表单时采用的是multipart/form-data
编码格式;multipart/form-data实际上是一种数据的编码分割方式,它支持我们以二进制形式发送数据;将文件数据与Form的表单数据一起传输,通过分割符(Boundary)来区分表单域与文件数据,这个分割符是在HTTP请求头里指定;
在上传文件时,只要在接收完毕HTTP请求头后,将后续的数据通过分割符区分即可,如果是文件数据,分隔符后的附加数据会指定 filename名。具体可以参考RFC1867文档
HTTP Headers {
"Accept-Language" = "zh-Hans-CN;q=1, en-CN;q=0.9, de-CN;q=0.8, ja-CN;q=0.7, zh-Hant-CN;q=0.6";
"Content-Length" = 88907;
"Content-Type" = "multipart/form-data; boundary=Boundary+7F78BD614AA37756";
"User-Agent" = "Alamofire/1.0.0 (iPhone; iOS 12.1.2; Scale/2.00)";
}
--Boundary+7F78BD614AA37756\r\n
Content-Disposition = form-data; name="filename"
<31353532 33353438 39343831 325f696f 732e6a70 67>
\r\n--Boundary+7F78BD614AA37756\r\n
Content-Disposition = form-data; name="subpath";
<2f666565 64626163 6b2f6957 4f574e2f 31353333 33353437 33343430 38313534 38332f>
\r\n--Boundary+7F78BD614AA37756\r\n
Content-Disposition = form-data; name="file"; filename="image.png";
Content-Type = "image/png";
<图片二进制数据>
\r\n--Boundary+7F78BD614AA37756--\r\n//有两个破折号放在后面表示结束
multipart / form-data
包含几个部分。每个部分都应包含一个Content-Disposition
头,它的值为form-data,name属性指定表单中的字段名称,每段数据都有一个可选的Content-Type,默认为text / plain,在传输文件的时候可以使用filename
参数来描述文件名,虽然不是必须的但是强烈推荐加上这个;
Content-Type
里面会有个boundary
指名分隔符传递给服务器,服务器根据此分隔符解析数据。上面的数据就是根据boundary划分段,每一段表示一段数据;(\r表示回车 \n表示换行)
大概了解了原理之后再来看看AFNetWorking的文件上传处理
- AFHTTPBodyPart:封装着用boundary分隔的其中一段数据,在上面就是一个
--Boundary+7F78BD614AA37756\r\n
加Content-Disposition = form-data; name="filename"
和表单字段name对应的具体数据; - AFMultipartBodyStream:保存着这个上传的所有数据,负责标记每个AFHTTPBodyPart先后顺序,同时内部使用
NSInputStream
读取文件数据; - AFMultipartFormData:定义了一组上传文件时添加文件数据的接口;
- AFStreamingMultipartFormData:接收一个request,将需要上传的数据处理成AFHTTPBodyPart添加到request的HTTPBodyStream里面,并设置相对应的请求头;
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(NSDictionary *)parameters
constructingBodyWithBlock:(void (^)(id formData))block
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(![method isEqualToString:@"GET"] && ![method isEqualToString:@"HEAD"]);
NSMutableURLRequest *mutableRequest = [self requestWithMethod:method URLString:URLString parameters:nil error:error];
__block AFStreamingMultipartFormData *formData = [[AFStreamingMultipartFormData alloc] initWithURLRequest:mutableRequest stringEncoding:NSUTF8StringEncoding];
if (parameters) {
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
NSData *data = nil;
if ([pair.value isKindOfClass:[NSData class]]) {
data = pair.value;
} else if ([pair.value isEqual:[NSNull null]]) {
data = [NSData data];
} else {
data = [[pair.value description] dataUsingEncoding:self.stringEncoding];
}
//这里会把参数封装成AFHTTPBodyPart保存起来
if (data) {
[formData appendPartWithFormData:data name:[pair.field description]];
}
}
}
//constructingBody block 通常就是通过这个block添加需要上传的文件
if (block) {
block(formData);
}
return [formData requestByFinalizingMultipartFormData];
}
- (NSMutableURLRequest *)requestByFinalizingMultipartFormData {
if ([self.bodyStream isEmpty]) {
return self.request;
}
[self.bodyStream setInitialAndFinalBoundaries];
//设置request的httpbodystream和header
[self.request setHTTPBodyStream:self.bodyStream];
[self.request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", self.boundary] forHTTPHeaderField:@"Content-Type"];
[self.request setValue:[NSString stringWithFormat:@"%llu", [self.bodyStream contentLength]] forHTTPHeaderField:@"Content-Length"];
return self.request;
}
POST上传文件一般会走到上面这个方法,将参数和所上传文件的数据保存封装成AFHTTPBodyPart
储存起来,并设置request的httpbodystream和header;
setHTTPBodyStream
参数是没有打开过的inputStream,并且设置之后会清空之前HTTPBody里面的所有数据;
NSStream
和CFStream
是toll-free Bridgin
的关系,CFStream
所在的位置和与CFNetwork
的关系可以看苹果官方给的结构图;
CFStream
可能会遇到阻塞,需要用轮询或者添加到runloop中进行流的调用,在这里用scheduleInRunLoop:forMode:
添加到runloop中;
- (BOOL)transitionToNextPhase {
if (![[NSThread currentThread] isMainThread]) {
//只能在主线程中串行调度
dispatch_sync(dispatch_get_main_queue(), ^{
[self transitionToNextPhase];
});
return YES;
}
switch (_phase) {
case AFEncapsulationBoundaryPhase:
_phase = AFHeaderPhase;
break;
case AFHeaderPhase:
//添加到runloop后open
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.inputStream open];
_phase = AFBodyPhase;
break;
case AFBodyPhase:
[self.inputStream close];
_phase = AFFinalBoundaryPhase;
break;
case AFFinalBoundaryPhase:
default:
_phase = AFEncapsulationBoundaryPhase;
break;
}
_phaseReadOffset = 0;
return YES;
}
取出流中第二个参数(length)中指定的字节数,并将它们放在客户端提供的缓冲区中(第一个参数)。缓冲区必须是第二个参数指定的大小,返回值是缓冲区中的实际字节数;如果流中没有任何内容则返回0。
- (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)length {
if ([self streamStatus] == NSStreamStatusClosed) {
return 0;
}
NSInteger totalNumberOfBytesRead = 0;
while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
break;
}
} else {
NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead;
//将每个AFHTTPBodyPart内容写入buffer中
NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];
if (numberOfBytesRead == -1) {
self.streamError = self.currentHTTPBodyPart.inputStream.streamError;
break;
} else {
totalNumberOfBytesRead += numberOfBytesRead;
if (self.delay > 0.0f) {
[NSThread sleepForTimeInterval:self.delay];
}
}
}
}
return totalNumberOfBytesRead;
}
AFURLRequestSerialization
AFHTTPResponseSerializer
负责校验response的状态码和content-type,
AFXMLResponseSerializer
、AFJSONResponseSerializer
和AFPropertyListResponseSerializer
顾名思义用来解析各自相关的数据;主要是来看看AFImageResponseSerializer
;
AFImageResponseSerializer
AFImageResponseSerializer专门是用来解析image数据,启用后是默认在非主线程中解码图片的;
static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
UIImage *image = [UIImage af_safeImageWithData:data];
if (image.images) {
return image;
}
return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
}
static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
CGImageRef imageRef = NULL;
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
if ([response.MIMEType isEqualToString:@"image/png"]) {
imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
} else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {
imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
if (imageRef) {
CGColorSpaceRef imageColorSpace = CGImageGetColorSpace(imageRef);
CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(imageColorSpace);
if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
CGImageRelease(imageRef);
imageRef = NULL;
}
}
}
CGDataProviderRelease(dataProvider);
UIImage *image = AFImageWithDataAtScale(data, scale);
if (!imageRef) {
if (image.images || !image) {
return image;
}
imageRef = CGImageCreateCopy([image CGImage]);
if (!imageRef) {
return nil;
}
}
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
//这里是大图不解码?
if (width * height > 1024 * 1024 || bitsPerComponent > 8) {
CGImageRelease(imageRef);
return image;
}
size_t bytesPerRow = 0;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
if (colorSpaceModel == kCGColorSpaceModelRGB) {
uint32_t alpha = (bitmapInfo & kCGBitmapAlphaInfoMask);
if (alpha == kCGImageAlphaNone) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
} else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
}
}
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
if (!context) {
CGImageRelease(imageRef);
return image;
}
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), imageRef);
CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];
CGImageRelease(inflatedImageRef);
CGImageRelease(imageRef);
return inflatedImage;
}
和SDWebImage解码操作没什么区别,都是通过获取bitmap上下文创建CGImageRef,然后生成图片;
AFSecurityPolicy
看这个模块需要一点TLS/SSL的基础知识;传输层安全协议(TLS)和前身安全套接字层(SSL)是现在HTTPS依赖的通信安全加密协议;
在HTTP通信前需要通过TLS握手协商双方通信的对话秘钥,然后再通过对话秘钥加密通信内容进行对话。
- ClientHello
客户端会向服务端以明文方式提供客户端 SSL 协议的版本号,加密算法的种类,和一个产生的随机数(randomA); - ServerHello
服务器向客户端传送 SSL 协议的版本号,加密算法的种类,随机数(randomB)以及其他相关信息,同时服务器还将向客户端传送自己的证书。如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供客户端证书(如果有这一步称为双向认证,没有则是单向认证); - 客户端回应
客户段根据服务器传过来的信息验证服务器的合法性,服务器的合法性包括:证书是否过期,发行服务器证书的 CA 是否可靠,发行者证书的公钥能否正确解开服务器证书的“发行者的数字签名”,服务器证书上的域名是否和服务器的实际域名相匹配。如果合法性验证没有通过, 通讯将断开;
如果证书没有问题,客户端计算产生随机数字 Pre-master,同时还会从证书中取出服务器的公钥并用公钥加密随机数字Pre-master,发送给服务器;。
此时客户端已经获取全部的计算协商密钥需要的信息:两个明文随机数 randomA 和 randomB 与自己计算产生的 Pre-master,计算得到协商密钥;
Change Cipher Spec:客户端通知服务器后续的通信都采用协商的通信密钥和加密算法进行加密通信;
Encrypted Handshake Message:结合之前所有通信参数的 hash 值与其它相关信息生成一段数据,采用协商密钥与加密算法进行加密,然后发送给服务器用于数据与握手验证; - 服务器响应
服务器用私钥解密加密的 Pre-master 数据,基于之前交换的两个明文随机数 randomA 和 randomB,计算得到协商密钥;
计算之前所有接收信息的 hash 值,然后解密客户端发送的Encrypted Handshake Message,验证数据和密钥正确性;
ChangeCipherSpec: 验证通过之后,服务器同样发送ChangeCipherSpec 以告知客户端后续的通信都采用协商的密钥与算法进行加密通信;
EncryptedHandshakeMessage, 服务器也结合所有当前的通信参数信息生成一段数据并采用协商密钥与加密算法加密并发送到客户端; - 握手结束
客户端计算所有接收信息的 hash 值,并采用协商密钥解密 Encrypted Handshake Message,验证服务器发送的数据和密钥,验证通过则握手完成; - 加密通信
开始使用协商密钥与算法进行加密通信。
可以看到顶级证书中心为
DigiCert Global Root CA
,二级证书中心DigiCert SHA2 Secure Server CA
,域名认证所有人*.jianshu.com
;
域名认证(Domain Validation):最低级别认证,可以确认申请人拥有这个域名。对于这种证书,浏览器会在地址栏显示一把锁。
在Mac的钥匙串访问中的系统根证书能找的到DigiCert Global Root CA
,所以此证书是可信的;
AFSecurityPolicy
封装了SSL身份挑战验证客户端证书的过程,先看看使用方法;
AFHTTPSessionManager *mgr = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://xxx.xxx.com"]];
NSSet *dataSet = [NSSet setWithArray:@[[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"y2ss.cer" ofType:nil]]]];
AFSecurityPolicy *policy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
policy.pinnedCertificates = dataSet;
policy.allowInvalidCertificates = YES;//是否使用无效或过期的证书信任服务器 默认为NO。
policy.validatesDomainName = NO;//是否验证证书的domain 默认为YES
mgr.securityPolicy = policy;
将cer文件拖进项目中,从Bundle加载data付值给AFSecurityPolicy
;共有三种AFSSLPinningMode
- AFSSLPinningModeNone
不使用客户端证书验证服务端 - AFSSLPinningModePublicKey
根据客户端证书的公钥验证服务器证书 - AFSSLPinningModeCertificate
根据客户端证书验证服务器证书
static id AFPublicKeyForCertificate(NSData *certificate) {
id allowedPublicKey = nil;
SecCertificateRef allowedCertificate;
SecPolicyRef policy = nil;
SecTrustRef allowedTrust = nil;
SecTrustResultType result;
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
__Require_Quiet(allowedCertificate != NULL, _out);//assertion不通过则goto到_out
policy = SecPolicyCreateBasicX509();
__Require_noErr_Quiet(SecTrustCreateWithCertificates(allowedCertificate, policy, &allowedTrust), _out);
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
_out:
if (allowedTrust) {
CFRelease(allowedTrust);
}
if (policy) {
CFRelease(policy);
}
if (allowedCertificate) {
CFRelease(allowedCertificate);
}
return allowedPublicKey;
}
SecCertificateCreateWithData
:返回表示证书对象,如果certificate data不是X.509证书则返回NULL;
SecPolicyCreateBasicX509
:创建X509证书信任策略对象,X.509 是一种证书格式,对X.509证书来说,认证者总是CA或由CA指定的人,X.509证书是一些标准字段的集合,这些字段包含有关用户或设备及其相应公钥的信息。X.509证书文档
SecTrustCreateWithCertificates
:判断证书(param1)是否符合信任策略(param2),返回allowedTrust(param3)表示x509证书的信任评估对象;
SecTrustEvaluate
:评估证书的有效性,对于每个策略,SecTrustEvaluate根据请求的策略构造证书链。然后,它以叶证书开始(在上面是*jianshu.com
),依次检查链中的每个证书,返回SecTrustResultType
判断证书是否有效;
SecTrustCopyPublicKey
:返回叶子结点证书的公钥;
综上所述AFPublicKeyForCertificate
的作用是根据我们传入的证书文件data获取它的公钥;
当我们调用policy.pinnedCertificates = dataSet;
时他会同时储存所有传入证书的公钥;
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
//需要验证domain 返回策略对象评估SSL证书链;
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
//替换服务器返回的信任政策
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
//AFServerTrustIsValid 本质是使用SecTrustEvaluate
//直接略过ssl验证或验证服务器返回的信任评估对象
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
//服务器返回的信任评估对象不符合客户端的信任策略 且
//不允许略过ssl验证则返回身份认证失败
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
//客户端证书
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
//设置客户端证书作为验证对象
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);
if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
// 保留验证通过的证书链数据
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
//如果证书链包含本地证书 验证通过
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
//获取服务器信任评估对象的公钥
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
//查看本地证书公钥是否对的上服务器返回的公钥
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
AFSecurityPolicy
最核心的方法,根据客户端的配置去验证服务端返回的SecTrustRef
(X509证书的信任评估对象),代码不多使用的都是系统级别的Security框架提供的api;
AFNetworkReachabilityManager
该模块用于监控网络状态,参照的是苹果官方Demo,封装实现的代码也就200多行,所以这里就大概的瞄一眼;
+ (instancetype)manager {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address;
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
return [self managerForAddress:&address];
}
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
//在init时已经retain了reachability 所以这里需要release释放
CFRelease(reachability);
return manager;
}
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
可以看到AFNetworkReachabilityManager
内部处理了ipv4和ipv6地址,只不过现在AFNetWorking(3.2.1)版本中最低支持系统版本为8.0,所以走的还是监控ipv4;
当
SCNetworkReachabilityCreateWithAddress
收到的参数为0.0.0.0的时候表示查询本机的网络连接状态;
- (void)startMonitoring {
[self stopMonitoring];
if (!self.networkReachability) {
return;
}
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
//参数2传的是ctx指定的数据块,在这里是一个指向block的指针
//参数3和参数4是参数2的retain和release回调函数指针
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
}
static const void * AFNetworkReachabilityRetainCallback(const void *info) {
//这里和下面的info都是指的是上面方法中的callback函数指针
return Block_copy(info);
}
static void AFNetworkReachabilityReleaseCallback(const void *info) {
if (info) {
Block_release(info);
}
}
static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) {
AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info);
}
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
if (block) {
block(status);
}
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
创建ctx绑定数据块(block)即是外面传入的callback(
setReachabilityStatusChangeBlock
设置的block),因为ctx不属于arc管理所以需要对block手动retain和release;-
通过
SCNetworkReachabilitySetCallback
给SCNetworkReachabilityRef
绑定当网络连接状态改变的回调;并且使用SCNetworkReachabilityScheduleWithRunLoop
添加到commonMode的runloop中;
可以看到是由source0触发的网络Reachability改变的回调,在收到这个回调之前先是收到
AFNetworkReachabilityRetainCallback
这个回调,再到AFNetworkReachabilityCallback
,其中info指向的就是ctx绑定的block(执行block是在主线程中完成的),之后通知外部我们传入的callback,最后执行AFNetworkReachabilityReleaseCallback
回调;
AFURLSessionManager
AFURLSessionManager
内部大概是这个样子,先从AFURLSessionManagerTaskDelegate
看起;
AFURLSessionManagerTaskDelegate
- (instancetype)initWithTask:(NSURLSessionTask *)task {
self = [super init];
if (!self) { return nil; }
_mutableData = [NSMutableData data];
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ]) {
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES;
progress.cancellationHandler = ^{
[weakTask cancel];
};
progress.pausable = YES;
progress.pausingHandler = ^{
[weakTask suspend];
};
#if AF_CAN_USE_AT_AVAILABLE
if (@available(iOS 9, macOS 10.11, *))
#else
if ([progress respondsToSelector:@selector(setResumingHandler:)])
#endif
{
progress.resumingHandler = ^{
[weakTask resume];
};
}
[progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
初始化了一个uploadProgress一个downloadProgress,用于记录上传和下载的进度,使用progress自带的各种handler控制操作request task,同时还注册kvo用来监听fractionCompleted(任务已完成单元/任务总单元),就是为了提供upload和download进度改变的callback;
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
NSData *data = nil;
if (self.mutableData) {
data = [self.mutableData copy];
self.mutableData = nil;
}
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
}
因为提供给NSURLSession的参数delegateQueue是个串行队列,所以在NSURLSessionTaskDelegate
回调的时候将在解析response放在并行队列,最后默认通过主队列回调出去;
- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
self.downloadProgress.totalUnitCount = dataTask.countOfBytesExpectedToReceive;
self.downloadProgress.completedUnitCount = dataTask.countOfBytesReceived;
[self.mutableData appendData:data];
}
下载和上传进度的处理;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.uploadProgress.completedUnitCount = task.countOfBytesSent;
}
AFURLSessionManagerTaskDelegate
是对一个NSURLSessionTask
的封装,在其基础上提供了上传下载进度管理以及task回调的处理;
_AFURLSessionTaskSwizzling
这个类的功能作者在注释里面已经阐述的非常详尽了,就是为了监听NSURLSessionTask的state属性;
+ (void)load {
if (NSClassFromString(@"NSURLSessionTask")) {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
while (class_getInstanceMethod(currentClass, @selector(resume))) {
Class superClass = [currentClass superclass];
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
if (classResumeIMP != superclassResumeIMP &&
originalAFResumeIMP != classResumeIMP) {
[self swizzleResumeAndSuspendMethodForClass:currentClass];//currentClass: __NSCFURLSessionTask, superClass: NSURLSessionTask
}
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}
}
创建一个临时的NSURLSessionDataTask
对象获取找到resume和suspend的imp指针,替换成下面两个;
- (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];
}
}
AFURLSessionManager
NSURLSeesion类簇关系图,后面框架会用到相关代理的方法;
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
可以看到初始化的基本配置,创建session的回调队列是串行队列,responseSerializer默认为json,以及证书和网络检测器的初始化,需要注意的是getTasksWithCompletionHandler
,这个方法获取session里面未完成的task并添加到任务队列里面,从后台进入前台恢复session的时候处理session未处理完成的task;
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler {
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task {
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
根据外部的request生成一个AFURLSessionManagerTaskDelegate
并给它注册通知,用一个map保存起来;uploadTask和downloadTask也是如此;
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error {
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
session失效时会收到此代理消息,通常是通过调用finishTasksAndInvalidate
或invalidateAndCancel
,前者会等待该session所有任务完成后者立即调用该代理,这里将该代理转发出来;
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
//这里是暴露出给外部自己实现校验的回调
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
//NSURLAuthenticationMethodServerTrust:收服务器信任的证书
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
//通过AFSecurityPolicy对象定义的规则处理来自服务器的证书
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
//创建一个凭证用于身份验证 disposition代表处理证书的方式
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
//安装证书
if (completionHandler) {
completionHandler(disposition, credential);
}
}
该代理通常是在服务器要求客户端进行身份校验或者是session建立SSL/TLS连接请求时验证服务器提供的证书链时调用;
NSURLAuthenticationChalleng
表示一个身份验证对象,里面封装了受保护空间(证书),受保护空间包含了主机、端口、协议等;
根据AFSecurityPolicy对象定义的校验规则来判断服务器返回的证书是否能通过身份挑战;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler {
NSURLRequest *redirectRequest = request;
if (self.taskWillPerformHTTPRedirection) {
redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request);
}
if (completionHandler) {
completionHandler(redirectRequest);
}
}
这里提供了拦截HTTP重定向的接口;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler {
NSInputStream *inputStream = nil;
if (self.taskNeedNewBodyStream) {
inputStream = self.taskNeedNewBodyStream(session, task);
} else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) {
inputStream = [task.originalRequest.HTTPBodyStream copy];
}
if (completionHandler) {
completionHandler(inputStream);
}
}
该代理要求提供输入流,有两种情况会调用:
1.通过uploadTaskWithStreamedRequest
创建task的;
2.任务因身份验证挑战或其他可恢复服务器错误而需要重新发送需要提供stream的请求任务;
这里如果没有实现taskNeedNewBodyStream
则会在request的httpbodystream中查找;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
int64_t totalUnitCount = totalBytesExpectedToSend;
//获取不到有效的传输体大小
if(totalUnitCount == NSURLSessionTransferSizeUnknown) {
//尝试从HTTP Header里面获取
NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"];
if(contentLength) {
totalUnitCount = (int64_t) [contentLength longLongValue];
}
}
//获取map中储存的AFURLSessionManagerTaskDelegate对象
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
if (delegate) {
//交给AFURLSessionManagerTaskDelegate处理
[delegate URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
}
//提供一个block回调
if (self.taskDidSendBodyData) {
self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount);
}
}
这个代理在上传数据的时候定期回调,通过它来掌握上传进度;
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
if (delegate) {
//同样分发到对应delegate对象里面处理
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];//清除task和通知
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
该代理在task已经完成时调用,这里的error仅仅是客户端的error,比如客户端无法解析host或无法连接到主机;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
if (self.dataTaskDidReceiveResponse) {
disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
}
if (completionHandler) {
completionHandler(disposition);
}
}
当收到服务器的response调用该代理,提供一个block可以使用者决定是否继续接受该task的数据;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask: (NSURLSessionDownloadTask *)downloadTask {
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
if (delegate) {
//移除旧的task替换新的task
[self removeDelegateForTask:dataTask];
[self setDelegate:delegate forTask:downloadTask];
}
if (self.dataTaskDidBecomeDownloadTask) {
self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
}
}
当在URLSession:dataTask:didReceiveResponse:completionHandler:delegate()
方法使用NSURLSessionResponseBecomeDownload
配置将请求转换为使用下载时就会调用该代理,在此调用代理之后,session delegate将不再接收与原始数据任务相关的delegate方法调用;
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
if (self.dataTaskDidReceiveData) {
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
task已接收到一些预期的数据的回调。因为数据对象参数通常由许多不同的数据对象拼凑而成,所以只要可能,就使用enumerateByteRangesUsingBlock:
方法来遍历数据,而不是使用bytes方法(它将数据对象扁平化为单个内存块)。此delegate可以被多次调用,并且每次调用只提供自上次调用以来接收到的数据。如果需要,应用程序负责积累这些数据。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
NSCachedURLResponse *cachedResponse = proposedResponse;
if (self.dataTaskWillCacheResponse) {
cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
}
if (completionHandler) {
completionHandler(cachedResponse);
}
}
询问代理data/upload task是否应将response存储在缓存中。
session在任务接收完所有预期数据后调用此代理方法,如果不实现此方法,默认行为是使用session配置对象中指定的缓存策略。
只有在处理请求的NSURLProtocol决定缓存响应时才调用此方法。一般来说,只有当下列各项都为真时,才会缓存响应:
1.请求是针对HTTP或HTTPS URL(或支持缓存的自定义网络协议)。
2.请求成功(状态码在200-299范围内)。
3.所提供的响应来自服务器,而不是缓存。
4.session配置的缓存策略允许缓存。
5.提供的URLRequest对象的缓存策略(如果适用)允许缓存。
6.服务器响应中的缓存相关头(如果存在)允许缓存。
7.响应大小足够小,可以合理地放入缓存中。(例如,如果提供磁盘缓存,响应必须不大于磁盘缓存大小的5%左右。)
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
if (self.didFinishEventsForBackgroundURLSession) {
dispatch_async(dispatch_get_main_queue(), ^{
self.didFinishEventsForBackgroundURLSession(session);
});
}
}
当session是后台模式时且APP进入到后台时,后台任务会继续进行,等到所有任务结束后则会调用此代理通知;再此之前先会调用AppDelegate的application:handleEventsForBackgroundURLSession:completionHandler:
,通常做法是储存系统下发的completionHandler
,等session结束所有任务后处理相关ui更新、清理垃圾等工作后再掉用系统的completionHandler
;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (self.downloadTaskDidFinishDownloading) {
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
//从临时下载位置移到自定义下载位置
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
在download task完成任务后调用该代理,如果实现了downloadTaskDidFinishDownloading
则会帮我们从临时储存地址移动到我们指定的地址;
AFURLSessionManager主要的工作就是处理与NSURLSession打交道的工作,包括dataTask的创建和resume,session代理的回调封装,提供一些关键节点的hook等;
AFHTTPSessionManager
AFHTTPSessionManager继承AFURLSessionManager,在它的基础上提供了一些便捷方法,在大部分项目中没有特殊需求的话可能都只是对它提供的GET、POST请求再做一层封装;
Extension
AFNetworking还提供了一些对UIKit的扩展,因为篇幅的限制所以把这段删了;其中主要可以看下AFImageDownloader
,是一个图片下载简易框架,虽然没有SDWebImage功能提供的丰富,虽然我们可能绝大部分都不会用AFImageDownloader
,但是这个下载器封装的实现、线程的管理是值得我们一看的;
总结
总的来说AFNetworking框架代码量不是很多也并不复杂,但是在模块的设计和封装是很值得借鉴和学习的,同时还需要了解和掌握基本的网络开发知识包括HTTP协议、SSL协议、NSURLSession的基本使用方法等,所以说阅读高质量的框架确实对提高自己写代码的水平和知识面的扩展是有很大帮助的;(因为篇幅的限制写了又删了一些东西这有些蛋疼);
参考和一些图片来源以及延伸阅读
图解SSL/TLS协议
SSL/TLS协议运行机制的概述
HTTPS加密协议详解(四):TLS/SSL握手过程
HTTPS Server Trust Evaluation
Networking Programming Topics
NSURLSession简介