//=========================第一部分===============================
在AFN框架中,负责网络请求通讯最重要的两个类AFURLSessionManager、AFHTTPSessionManager。AFHTTPSessionManager又是继承于AFURLSessionManager,进行了封装。
AFURLRequestSerialization结构的类,主要用于网络请求之前的操作配置,负责配置网络请求的请求头部、序列化请求参数
AFURLResponseSerialization结构的类,主要用于网络请求之后的数据处理,针对不同的数据进行处理,比如JSON、XML、plist、图片格式等数据。
AFSecurityPolicy主要用于HTTPS环境下的认证安全请求通讯。如果是通过CA认证过的HTTPS访问地址,使用AFN时只需要拼接上https://即可,AFN的网络请求配置中默认使用CA认证访问HTTPS地址;若是自签的证书时,则需要当前类用于进行认证。
AFNetworkReachabilityManager,用于网络状态的监听,判断是否有网络,以及判断网络连接类型,比如蜂窝网络或WiFi环境。但当前类无法判断当前环境能否访问服务器服务。其原理是利用主机的数据包发送。
一、AFURLSessionManager
AFURLSessionManager是AFHTTPSessionManger的基类。它遵循了NSURLSession 和 NSURLSessionTask的一些代理方法,实现了数据的请求、上传和下载功能。
预览一下.h文件,我们可以看到下面这四个只读属性:
@property(readonly,nonatomic,strong)NSArray *tasks;
@property(readonly,nonatomic,strong)NSArray *dataTasks;
@property(readonly,nonatomic,strong)NSArray *uploadTasks;
@property(readonly,nonatomic,strong)NSArray *downloadTasks;
分别代表了总的任务集合、数据任务集合、上传任务集合和下载任务集合。
然后我们再看下几个创建请求任务的方法:
1.创建数据任务
- (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;
2.创建上传任务(有三种方法)
通过fileURL方式上传(上传本地文件的URL路径)
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
通过bodyData方式上传(数据放在请求体httpBody中)
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(nullable NSData *)bodyData
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
通过流方式上传(此时一定要设置setTaskNeedNewBodyStreamBlock回调,否则session没办法在重新发送steam的时候找到数据源)
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
3.创建下载任务(也有两种方式)
通过http方式普通下载
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
另一种则是通过之前的下载数据来恢复下载,destination在下载的过程中文件会先存放在一个临时的位置,等到下载完成之后,文件会转移到目标位置
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
其他一些方法都是设置请求相关的,比如请求失败策略(是否继续请求),获取上传、下载进度progress等。
再来看下.m实现,实现文件里面具体分为三部分:
第一部分:AFURLSessionManagerTaskDelegate
Delegate里面做的内容有:
1、设置task的progress
2、KVO监听task、progress的方法调用
3、实现URLSessionTask的回调,将完成的结果block给manager里面相关的调用方法,并且通知给接收者
4、对接收到的数据进行了拼接
5、对下载完成后的文件进行转移
第二部分:_AFURLSessionTaskSwizzling
这部分主要做的是将NSURLSessionDataTask及父类里的resume和suspend方法替换成自己自定义的方法
第三部分:AFURLSessionManager
1、初始化session配置及增加task代理,通过self.session getTasks得到所有的任务,然后依次设置代理
2、外部API的block通过delegate里面的block来获得
//===========================第二部分=============================
AFHTTPSessionManager是继承于AFURLSessionManager,提供了几个更方便的http请求的方法,GET、POST、PUT、PATCH、DELETE。在这里我们可以看到他有一个readOnly修饰的baseURL属性,我们可以在二次封装的时候设置成我们项目中要使用的地址(一般我们使用AF的时候都会进行二次封装,这样更方便来满足我们的需求)。
这里我们可以看到下面这几个初始化方法,主要是设置baseURL、requestSerializer和responseSerializer,requestSerializer和responseSerializer我们在接下来的几篇文章里会介绍。
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
- (instancetype)init {
return [self initWithBaseURL:nil];
}
- (instancetype)initWithBaseURL:(NSURL *)url {
return [self initWithBaseURL:url sessionConfiguration:nil];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
return [self initWithBaseURL:nil sessionConfiguration:configuration];
}
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
然后下面我们可以看到POST、GET、PUT、PATCH、DELETE这五个方法。他们的传参基本都是大同小异,URLString表示请求的URL,parameters表示客户端请求内容的存储器,progress表示请求的进度,constructingBodyWithBlock里面只有一个formData用来拼接到HTTP的请求体,success表示请求成功后的block回调,failure表示请求失败的block回调。那么他们有什么不同呢?
1、POST请求是向服务端发送数据的,用来更新资源信息,它可以改变数据的种类等资源
2、GET请求是向服务端发起请求数据,用来获取或查询资源信息
3、PUT请求和POST请求很像,都是发送数据的,但是PUT请求不能改变数据的种类等资源,它只能修改内容
4、DELETE请求就是用来删除某个资源的
5、PATCH请求和PUT请求一样,也是用来进行数据更新的,它是HTTP verb推荐用于更新的。
这几个方法的实现其实都是下面那个[dataTaskWithHTTPMethod:URLString:parameters:uploadProgress:downloadProgress:success:failure]。传参的内容基本都是和上一层方法一样,method指的就是请求的类型,返回的是NSURLSessionDataTask类型的对象。
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
for (NSString *headerField in headers.keyEnumerator) {
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
//===========================第三部分=============================
AFSecurityPolicy
在iOS9后苹果默认是不能使用HTTP请求的,而AFSecurityPolicy主要的作用就是验证HTTPS请求的证书的有效性。如果你还是想要使用HTTP请求,就需要在plist里面设置NSAppTransportSecurity的NSAllowsArbitraryLoads为true,当然我们首先是推荐使用HTTPS的。
AFSecurityPolicy是安全策略类,有三种SSL Pinning模式。
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
};
这个是证书集合,泛型里面表示了集合里面是NSData类型,表明这个是用来存证书数据的集合,这些证书根据SSL Pinning模式来和服务器进行校验,默认是没有证书的,我们需要调用+ certificatesInBundle:方法将bundle里面的证书文件转成里面是data类型的集合
@property (nonatomic, strong, nullable) NSSet *pinnedCertificates;
+ (NSSet *)certificatesInBundle:(NSBundle *)bundle {
// 获取证书
NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."];
NSMutableSet *certificates = [NSMutableSet setWithCapacity:[paths count]];
// 将证书文件转成data
for (NSString *path in paths) {
NSData *certificateData = [NSData dataWithContentsOfFile:path];
[certificates addObject:certificateData];
}
return [NSSet setWithSet:certificates];
}
有三种初始化的方法
第一种是默认策略,AFSSLPinningModeNone
+ (instancetype)defaultPolicy {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
return securityPolicy;
}
第二种是自定义一个安全策略,然后读取cer文件放到集合里面
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode {
return [self policyWithPinningMode:pinningMode withPinnedCertificates:[self defaultPinnedCertificates]];
}
+ (NSSet *)defaultPinnedCertificates {
static NSSet *_defaultPinnedCertificates = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
_defaultPinnedCertificates = [self certificatesInBundle:bundle];
});
return _defaultPinnedCertificates;
}
第三种则是需要我们多传入一个证书集合
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = pinningMode;
[securityPolicy setPinnedCertificates:pinnedCertificates];
return securityPolicy;
}
设置证书的时候,就是把上面初始化时传入的证书取出公钥,再把公钥保存到mutablePinnedPublicKeys集合中
- (void)setPinnedCertificates:(NSSet *)pinnedCertificates {
_pinnedCertificates = pinnedCertificates;
if (self.pinnedCertificates) {
NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]];
for (NSData *certificate in self.pinnedCertificates) {
// 取出公钥
id publicKey = AFPublicKeyForCertificate(certificate);
if (!publicKey) {
continue;
}
// 将公钥存到集合
[mutablePinnedPublicKeys addObject:publicKey];
}
self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys];
} else {
self.pinnedPublicKeys = nil;
}
}
在AFPublicKeyForCertificate方法里面,做了一系列操作后返回公钥,如下:
static id AFPublicKeyForCertificate(NSData *certificate) {
id allowedPublicKey = nil;
SecCertificateRef allowedCertificate;
SecCertificateRef allowedCertificates[1];
CFArrayRef tempCertificates = nil;
SecPolicyRef policy = nil;
SecTrustRef allowedTrust = nil;
SecTrustResultType result;
// 取出证书SecCertificateRef
allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate);
__Require_Quiet(allowedCertificate != NULL, _out);
// 生成证书数组
allowedCertificates[0] = allowedCertificate;
tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL);
// 生成SecPolicyRef
policy = SecPolicyCreateBasicX509();
__Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out);
__Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out);
// 从SecPolicyRef中取出公钥
allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust);
_out:
// 一些资源的释放
return allowedPublicKey;
}
-[evaluateServerTrust:forDomain:]方法是AFSecurityPolicy类最长也是最重要的方法,它用来验证服务端是否是受信
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
// 苹果文档中表示不要隐式地信任自己签名的证书,取而代之的是应该增加自己的CA证书到受信列表里
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;
}
// policies增加SecPolicyRef
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
// 设置信任的policies应当被验证
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
// 向系统内置的根证书验证服务端返回的证书是否合法
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
// 根据SSLPinningMode对服务端是否受信进行校验
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;
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
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;
}
//===========================第四部分=============================
AFNetworkReachabilityManager
AFNetworkReachabilityManager是用来监测网络状态的类。设置状态改变回调方法,当网络状态改变时获得当前网络状态回调过来。
网络的状态值有以下四种:
ypedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,// 未知
AFNetworkReachabilityStatusNotReachable = 0, // 不可用
AFNetworkReachabilityStatusReachableViaWWAN = 1, // 无线广域网连接
AFNetworkReachabilityStatusReachableViaWiFi = 2, // WiFi连接
};
AFNetworkReachabilityManager提供了五种初始化的方法。
可以通过单例方法初始化
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 调用+ manager初始化方法
_sharedManager = [self manager];
});
return _sharedManager;
}
单例里面调用了第二种通过默认的socket地址初始化方法,返回一个manager对象,sin_family表示协议族,AF_INET表示TCP/IP协议族的地址
+ (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
// 声明sockaddr_in结构体
struct sockaddr_in address;
// address清零
bzero(&address, sizeof(address));
// address赋值
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
// 调用+ [managerForAddress:]方法
return [self managerForAddress:&address];
}
在这里又调用了第三种通过传入一个socket地址来初始化
+ (instancetype)managerForAddress:(const void *)address {
// 生成SCNetworkReachabilityRef
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
// 调用- [initWithReachability:]方法
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
里面又调用了第五种初始化方法,因为该方法的后缀里面有NS_DESIGNATED_INITIALIZER,所以最终都会调到它,这里就是做了初始化的工作,将起始的网络状态定为Unknown
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}
最后剩下一种方法就是可以根据特定的域来初始化
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
然后在初始化结束之后,我们需要设置网络状态改变的回调,在开启监听之后,会将网络状态回调给外部
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block {
self.networkReachabilityStatusBlock = block;
}
最后就是开启监听
- (void)startMonitoring {
// 停止监听
[self stopMonitoring];
if (!self.networkReachability) {
return;
}
// 收到callback调用后,将status通过networkReachabilityStatusBlock回调出去
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
// 声明SCNetworkReachabilityContext结构体
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
// 设置回调
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
// 加到Main runloop里面对其进行监测
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
// 获取当前的网络状态,调用callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
}
如果没有设置回调的话,也可以通过注册通知的方式,收到网络状态的变化
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) {
// 获取当前的status
AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags);
dispatch_async(dispatch_get_main_queue(), ^{
// 返回status值
if (block) {
block(status);
}
// 同时会发送一个通知
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) };
[notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo];
});
}
停止监听的话,就是取消在Main Runloop里面的监听
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}
具体使用方法就如下:
AFNetworkReachabilityManager *networkManager = [AFNetworkReachabilityManager sharedManager];
[networkManager startMonitoring];
[networkManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusNotReachable:
// do something
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
// do something
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
// do something
break;
default:
break;
}
}];
//===========================第五部分=============================
AFURLRequestSerialization
AFURLRequestSerialization是用来对发出的请求进行一些处理
AFPercentEscapedStringFromString方法将string里面的:#[]@!$&’()*+,;=字符替换成%
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];
// 声明批量处理的大小为50
static NSUInteger const batchSize = 50;
NSUInteger index = 0;
NSMutableString *escaped = @"".mutableCopy;
// 循环将string里面:#[]@!$&'()*+,;=的字符替换成%
while (index < string.length) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu"
NSUInteger length = MIN(string.length - index, batchSize);
#pragma GCC diagnostic pop
NSRange range = NSMakeRange(index, length);
// To avoid breaking up character sequences such as
range = [string rangeOfComposedCharacterSequencesForRange:range];
NSString *substring = [string substringWithRange:range];
NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
[escaped appendString:encoded];
index += range.length;
}
return escaped;
}
在AFQueryStringPair类里面有个- URLEncodedStringValue方法,将请求里面的URL参数转成field=value形式
- (NSString *)URLEncodedStringValue {
if (!self.value || [self.value isEqual:[NSNull null]]) {
return AFPercentEscapedStringFromString([self.field description]);
} else {
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
}
}
字典里面是我们查询的key和value,我们通过将字典内容转成AFQueryStringPair对象,调用- URLEncodedStringValue方法,转成key=value,放到mutablePairs数组里,最后用&符拼接起来
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
// 如果是字典,遍历后返回key[nestedKey]=nestedValue
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} // 如果是数组,遍历后返回key[]=nestedValue
else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} // 如果是集合,遍历后返回key=obj
else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} // 其他返回key=value
else {
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}
在使用AFHTTPRequestSerializer对HTTP请求的头部进行处理的时候,我们首要要调用(+) serializer初始化,然后设置属性的监听,这些属性在头文件里面都可以找到,实现文件里面也实现了set方法
self.mutableObservedChangedKeyPaths = [NSMutableSet set];
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {
[self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];
}
}
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
return _AFHTTPRequestSerializerObservedKeyPaths;
}
通过KVO判断是否是新值,如果是的话,就加到mutableObservedChangedKeyPaths里面
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFHTTPRequestSerializerObserverContext) {
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}
设置验证字段
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
password:(NSString *)password
{
NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
[self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
}
初始化之后,需要调用
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 断言
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
// 根据url初始化request
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
// 设置HTTP方法
mutableRequest.HTTPMethod = method;
// 根据mutableObservedChangedKeyPaths存储的属性,设置到mutableRequest
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 调用- [requestBySerializingRequest:withParameters:error]方法
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
//===========================第五部分=============================
AFURLResponseSerialization
AFURLResponseSerialization是用来将返回的response处理成相应的格式,它通过协议对特定response的data进行解码。
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;
AFHTTPResponseSerializer可以通过+ serializer和- init方法进行初始化,实际上+ serializer内只是调用了- init
+ (instancetype)serializer {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
// 设置字符串编码类型,可接受的状态码,可接受的MIME类型
self.stringEncoding = NSUTF8StringEncoding;
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;
return self;
}
acceptableStatusCodes和acceptableContentTypes可以通过外部设置
@property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes;
@property (nonatomic, copy, nullable) NSSet *acceptableContentTypes;
然后可以调用- [validateResponse:data :error:]检查这个response是否包含可接受的状态码和可接受MIME类型来验证response的有效性,子类也可以增加特定域名检查,- [responseObjectForResponse:data:error]也是调用了这个方法,返回data
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 调用- [validateResponse:data:error:]方法,返回data
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
// 设置初始值
BOOL responseIsValid = YES;
NSError *validationError = nil;
// 检查这个response是否包含可接受的状态码和可接受MIME类型
if (error && !responseIsValid) {
*error = validationError;
}
// 返回response是否有效性
return responseIsValid;
}
检查这个response是否包含可接受的状态码和可接受MIME类型
// 检查response是否为空,以及response是否是NSHTTPURLResponse类
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
// acceptableContentTypes不为空并且response的MIME类型不在可接受的范围里
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]]) {
// 包装错误信息
if ([data length] > 0 && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
// acceptableStatusCodes不为空并且acceptableStatusCodes包含response的状态码,response的URL也存在
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
// 包装错误信息
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
但是这里有个疑问,假如response为nil或者response不是NSHTTPURLResponse类,那下面的操作均不会对responseIsValid布尔值进行修改,最后返回的是个YES,但是这样的response不应该是NO么?
AFJSONResponseSerializer是继承于AFHTTPResponseSerializer
外部可以设置NSJSONReadingOptions和是否移除空值的key
@property (nonatomic, assign) NSJSONReadingOptions readingOptions;
@property (nonatomic, assign) BOOL removesKeysWithNullValues;
转换object的时候,会检查data是否是空格,这个是Safari的一个bug,具体请看Workaround for behavior of Rails to return a single space for head :ok (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
id responseObject = nil;
NSError *serializationError = nil;
// 判断是否是空格
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length > 0 && !isSpace) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
// 调用AFJSONObjectByRemovingKeysWithNullValues把空值的key都移除掉,返回object
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
AFXMLParserResponseSerializer则是直接校验response后,用data初始化NSXMLParser对象并返回
- (id)responseObjectForResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
return [[NSXMLParser alloc] initWithData:data];
}
AFPropertyListResponseSerializer也是类似的处理
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
id responseObject;
NSError *serializationError = nil;
if (data) {
responseObject = [NSPropertyListSerialization propertyListWithData:data options:self.readOptions format:NULL error:&serializationError];
}
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error);
}
return responseObject;
}
AFImageResponseSerializer在验证response之后,会根据设置是否自动解压automaticallyInflatesResponseImage布尔值,来对imageData按图片比例返回UIImage对象
@property (nonatomic, assign) CGFloat imageScale;
@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage;
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
// iOS需要手动解压图片
if (self.automaticallyInflatesResponseImage) {
return AFInflatedImageFromResponseWithDataAtScale((NSHTTPURLResponse *)response, data, self.imageScale);
} else {
return AFImageWithDataAtScale(data, self.imageScale);
}
#else
// MacOS可以直接使用NSBitmapImageRep来解压
NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:data];
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])];
[image addRepresentation:bitimage];
return image;
#endif
return nil;
}
如果不解压的话,就直接根据imageData和scale来创建Image,但是这有个疑问是,AF为什么要创建两次image,我觉得可以直接使用- [imageWithData:scale:]方法
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];
}
但是如果用imageWithData转成UIImage对象后,由于网络图片PNG和JPG都是压缩格式,需要解压成bitmap后才能渲染到屏幕,这时会在主线程对图片进行解压操作,这是比较耗时的,可能还会对主线程造成阻塞,所以AF还提供了AFInflatedImageFromResponseWithDataAtScale方法,对PNG和JPG解压后,返回UIImage对象,这样避免了在主线程的解压操作,不会对主线程造成卡顿
static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
if (!data || [data length] == 0) {
return nil;
}
// 创建CGImageRef
CGImageRef imageRef = NULL;
// 用data创建CGDataProviderRef
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);
// 如果色彩空间是CMKY,CGImageCreateWithJPEGDataProvider是不会进行处理的,也就是不进行解压,将调用AFImageWithDataAtScale返回image
if (imageColorSpaceModel == kCGColorSpaceModelCMYK) {
CGImageRelease(imageRef);
imageRef = NULL;
}
}
}
CGDataProviderRelease(dataProvider);
// 不符合解压条件的,将调用AFImageWithDataAtScale返回image,但是这里如果符合解压条件的也会调用,以及下面会对超出大小的,直接返回image,这里我觉得应该统一对不符合条件的返回image,符合条件的就不需要调用AFImageWithDataAtScale
UIImage *image = AFImageWithDataAtScale(data, scale);
if (!imageRef) {
if (image.images || !image) {
return image;
}
// 这里调用CGImageCreateCopy,只会对图形本身结构进行拷贝,底层的数据是不会拷贝的
imageRef = CGImageCreateCopy([image CGImage]);
if (!imageRef) {
return nil;
}
}
// 设置图片的宽和高和存储一个像素所需要用到的字节
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
// 如果图片大小宽高乘积超过1024*1024或者bitsPerComponent大于8都不解压了,因为bitmap是一直存在UIImage对象里的,可能会把内存爆了
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);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wassign-enum"
if (alpha == kCGImageAlphaNone) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
} else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
}
#pragma clang diagnostic pop
}
// 创建画布
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
CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
// 再转成UIImage对象
UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:image.imageOrientation];
CGImageRelease(inflatedImageRef);
CGImageRelease(imageRef);
return inflatedImage;
}
AFCompoundResponseSerializer
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
{
// 遍历responseSerializers
for (id serializer in self.responseSerializers) {
// 如果serializer不是AFHTTPResponseSerializer类,则继续
if (![serializer isKindOfClass:[AFHTTPResponseSerializer class]]) {
continue;
}
NSError *serializerError = nil;
// 一层一层的调用自己的- [responseObjectForResponse:data:error:],直到返回responseObject
id responseObject = [serializer responseObjectForResponse:response data:data error:&serializerError];
if (responseObject) {
if (error) {
*error = AFErrorWithUnderlyingError(serializerError, *error);
}
return responseObject;
}
}
return [super responseObjectForResponse:response data:data error:error];
}