AFNetworking
组成
1. AFURLSessionManager 基于NSURLSession 负责请求(下载,上传等等)
code
基于遵循<NSURLSessionTaskDelegate>,
<NSURLSessionDataDelegate>,<NSURLSessionDownloadDelegate>和
<NSURLSessionDelegate>的指定NSURLSessionConfiguration对象创建和管
理NSURLSession对象
先创建管理器
NSURLSessionConfiguration *configuration =
[NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc]
initWithSessionConfiguration:configuration];
1.创建下载任务
NSURLSessionDownloadTask *downloadTask = [manager
downloadTaskWithRequest:request
progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse
*response) { }];
[downloadTask resume];//其他也是调用 resume方法开始开始执行
2.创建上传任务
NSURLSessionUploadTask *uploadTask = [manager
uploadTaskWithRequest:request
fromFile:filePath progress:^(NSProgress * _Nonnull uploadProgress)
completionHandler:^( ...
3.创建数据任务
NSURLSessionDataTask *dataTask = [manager
dataTaskWithRequest:request completionHandler:^(
2. Serialization 序列化器 : 对数据进行格式,
编码(字符串,字典,URL,JSON等等)
例如一个请求
URL
NSString *URLString = @"http://example.com";
参数
NSDictionary *parameters = @{@"foo": @"bar", @"baz": @[@1, @2, @3]};
序列化函数
发起一个GET 或者 POST 请求,都需要下面函数去序列化:
[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET or POST
or DELETE ...“ URLString:URLString parameters:parameters error:nil];
序列化结果
GET
http://example.com?foo=bar&baz[]=1&baz[]=2&baz[]=3
POST
http://example.com/
Content-Type: application/x-www-form-urlencoded
表单类型
foo=bar&baz[]=1&baz[]=2&baz[]=3
JSON
(POST) http://example.com/
Content-Type: application/json
{"foo": "bar", "baz": [1,2,3]}
另外还有 XML、Plist、image 解析器
3. Reachability 网络监控判断
获取当前网络状态
[[AFNetworkReachabilityManager sharedManager]
setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status)
{
NSLog(@"Reachability: %@",
AFStringFromNetworkReachabilityStatus(status));
}];
//开始检测
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
网络状态
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1, //未知
AFNetworkReachabilityStatusNotReachable = 0, //无连接
AFNetworkReachabilityStatusReachableViaWWAN = 1, //2(3,4)G
AFNetworkReachabilityStatusReachableViaWiFi = 2, //WIFI
4.Security HTTPS通信相关验证,配置证书
HTTPS 加密请求验证及封装
AFSecurityPolicy 通过配置ssl 加强网络安全性
GET请求
一个简单请求需要走哪些方法
请求任务 NSURLSessionDataTask
Task
1: - (nullable NSURLSessionDataTask *)GET:URLString parameters:
success:^{
} failure:^{
} ...
2: NSURLSessionDataTask *dataTask = [self
dataTaskWithHTTPMethod:@"GET" URLString:
parameters: uploadProgress:nil downloadProgress: success: failure:
最终都是调有Progress参数的方法, 调用没有Progress的方法,会默认置空nil
3: dataTask = [self dataTaskWithRequest:request uploadProgress:
downloadProgress: completionHandler
4.最后请求
创建request
NSMutableURLRequest *request = [self.requestSerializer
requestWithMethod:method
URLString:[...self.baseURL absoluteString] parameters: error:];
最后系统请求方法:
dataTask = [self.session dataTaskWithRequest:request];
任务代理AFURLSessionManagerTaskDelegate
源码
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task); //断言: task或者delegate为空就触发
NSParameterAssert(delegate);
[self.lock lock]; //加锁
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)
] = delegate;
[delegate setupProgressForTask:task]; //添加进度
[self addNotificationObserverForTask:task]; //添加任务(开始和暂停) 监
听
[self.lock unlock];
}
任务代理是如何监听进度的? (KVO)
答
首先观察一段代码:
- (void)setupProgressForTask:(NSURLSessionTask *)task {
__weak __typeof__(task) weakTask = task;
self.uploadProgress.totalUnitCount =
task.countOfBytesExpectedToSend;
... //任务进度的取消,暂停和开始 回调
[self.uploadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
... [strongTask suspend]; ... [strongTask resume];
...
//添加各种监听
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived
))
options:NSKeyValueObservingOptionNew
context:NULL];
...(@selector(countOfBytesExpectedToReceive)) ...
(@selector(countOfBytesSent))
... (@selector(fractionCompleted)) ...
}
AFURLSessionManagerTaskDelegate代理 主要为任务设置进度,并对进度进行
监听
AFURLSessionManager 如何通知监听任务 ?
(KVO)
答
常量:
NSString * const AFNetworkingTaskDidResumeNotification =
@"com.alamofire.networking.task.resume";
//添加KVO监听
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(taskDidResume:)
name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(taskDidSuspend:)
name:AFNSURLSessionTaskDidSuspendNotification object:task];
}
dispatch_async(dispatch_get_main_queue(), ^{ //通知, 其他状态一样,就是
名称不一样
[[NSNotificationCenter defaultCenter]
postNotificationName:AFNetworkingTaskDidResumeNotification
object:task];
});
对系统NSURLSession代理方法进行转发 (运行时)
转发
首先需要 重写respondsToSelector方法
源码
- (BOOL)respondsToSelector:(SEL)selector {
if (selector ==
@selector(URLSession:task:willPerformHTTPRedirection:newRequest:com
pletionHandler:)) {
return self.taskWillPerformHTTPRedirection != nil; //1. 如果为空,不
执行系统代理方法,下面也不要转发到自己的方法去
} else if (selector ==
@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)
) {
return self.dataTaskDidReceiveResponse != nil;
} else if (selector ==
@selector(URLSession:dataTask:willCacheResponse:completionHandler:))
{
return self.dataTaskWillCacheResponse != nil;
} else if (selector ==
@selector(URLSessionDidFinishEventsForBackgroundURLSession:)) {
return self.didFinishEventsForBackgroundURLSession != nil;
}
return [[self class] instancesRespondToSelector:selector]; //2. 转发到自
己的方法中
}
if (delegate) {
[delegate URLSession:session task:task
didCompleteWithError:error];
[self removeDelegateForTask:task];
}
解析
对没有为block赋值的状态进行判断
从NSURLSession原生的代理转发代理到AFURLSessionManagerTaskDelegate
自定义的代理中
HTTPS认证上AFSecurityPolicy做了啥 ?
认证
HTTP / HTTPS / ssl 等
详细描述参考<网络篇> 或 GG , Baidu
NSURLSessionDataDelegate
服务器验证客户端合法
web服务器接收到客户端请求时,有时候需要先验证客户端是否为正常用户,再
决定是够返回真实数据。这种情况称之为服务端要求客户端接收挑战
(NSURLAuthenticationChallenge *challenge)。
接收到挑战后,客户端要根据服务端传来的challenge来生成
completionHandler所需的NSURLSessionAuthChallengeDisposition
disposition和NSURLCredential *credential(disposition指定应对这个挑战的
方法,而credential是客户端生成的挑战证书,注意只有challenge中认证方法为
NSURLAuthenticationMethodServerTrust的时候,才需要生成挑战证书)。
最后调用completionHandler回应服务器端的挑战。
客户端验证服务器证书
当某个session使用SSL/TLS协议,第一次和服务器端建立连接的时候,服务器会
发送给iOS客户端一个证书,此方法允许你的app验证服务期端的证书链
(certificate keychain)
源码
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session,
challenge, &credential);
} else {
// 此处服务器要求客户端的接收认证挑战方法是
NSURLAuthenticationMethodServerTrust
// 也就是说服务器端需要客户端返回一个根据认证挑战的保护空间提供的信
任(即challenge.protectionSpace.serverTrust)产生的挑战证书。
// 而这个证书就需要使用credentialForTrust:来创建一个NSURLCredential
对象
if ([challenge.protectionSpace.authenticationMethod
isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 基于客户端的安全策略来决定是否信任该服务器,不信任的话,也就没
必要响应挑战
if ([self.securityPolicy
evaluateServerTrust:challenge.protectionSpace.serverTrust
forDomain:challenge.protectionSpace.host]) {
// 创建挑战证书(注:挑战方式为UseCredential和
PerformDefaultHandling都需要新建挑战证书)
credential = [NSURLCredential
credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition =
NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
//取消挑战
disposition =
NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
securityPolicy
这里securityPolicy存在的作用就是,使得在系统底层自己去验证之前,AF可以
先去验证服务端的证书。如果通不过,则直接越过系统的验证,取消HTTPS的网
络请求。否则,继续去走系统根证书的验证。
实例
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
属性
// 验证模式
@property (readonly, nonatomic, assign) AFSSLPinningMode
SSLPinningMode;
// 可以去匹配服务端证书验证的证书
@property (nonatomic, strong, nullable) NSSet <NSData *>
*pinnedCertificates;
// 是否支持非法的证书(例如自签名证书)
@property (nonatomic, assign) BOOL allowInvalidCertificates;
// 是否去验证证书域名是否匹配
@property (nonatomic, assign) BOOL validatesDomainName;
AFSSLPinningMode
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone, //不验证
AFSSLPinningModePublicKey, //只验证公钥
AFSSLPinningModeCertificate, // 验证证书
};
验证服务端
根据模式验证流程
AFSSLPinningModeNone
1 不验证 : 则肯定是返回YES,不论是自签还是公信机构的证书。
AFSSLPinningModeCertificate
2 验证证书 : 则从serverTrust中去获取证书链,然后和我们一开始初始化设置的
证书集合self.pinnedCertificates去匹配,如果有一对能匹配成功的,就返回
YES,否则NO
AFSSLPinningModePublicKey
3 公钥验证 : 则和第二步一样还是从serverTrust,获取证书链每一个证书的公
钥,放到数组中。和我们的self.pinnedPublicKeys,去配对,如果有一个相同
的,就返回YES,否则NO
付费公信机构颁发的证书
无论你用的是AF还是NSURLSession,什么都不用做,代理方法也不用实现。你
的网络请求就能正常完成
自签名的证书
首先你需要在plist文件中,设置可以返回不安全的请求(关闭该域名的ATS)
AFN
需要设置policy
//允许自签名证书
policy.allowInvalidCertificates = YES;
//是否验证域名的CN字段
policy.validatesDomainName = NO;
NSURLSesion
//证书挑战 则跑到这里
disposition = NSURLSessionAuthChallengeUseCredential;
1. 先AFN验证,过滤无效请求
1. AF可以让你在系统验证证书之前,就去自主验证。然后如果自己验证不正确,
直接取消网络请求。
2. 验证通过, 则继续进行系统验证。
2. 再系统的验证
1. 合法https
首先是去系统的根证书找,看是否有能匹配服务端的证书,如果匹配,则验证成
功,返回https的安全数据
系统会更新合法证书
2. 不安全https
如果不匹配则去判断ATS是否关闭,如果关闭,则返回https不安全连接的数据
3. 请求失败
如果开启ATS,则拒绝这个请求,请求失败
下载数据的代理方法
代理
也是NSURLSessionDataDelegate 来处理
代理: didReceiveResponse
c
- (void)URLSession: dataTask: didReceiveResponse: completionHandler:
content-type的类型为multipart/x-mixed-replace那么服务器的数据会分片
的传回来。然后这个方法是每次接受到对应片响应的时候会调被调用
代理: didBecomeDownloadTask
c
- (void)URLSession: dataTask:didBecomeDownloadTask:
内部
AFURLSessionManagerTaskDelegate *delegate = [self
delegateForTask:dataTask];
if (delegate) {
[self removeDelegateForTask:dataTask];
[self setDelegate:delegate forTask:downloadTask];
}
内部干啥了
新建一个downloadTask,替换掉当前的dataTask。
所以我们在这里做了AF自定义代理的重新绑定操作
[self setDelegate:delegate forTask:downloadTask];
代理: didReceiveData
获取到数据就会调用,会被反复调用,请求到的数据就在这被拼装完整
代理: willCacheResponse
询问data task或上传任务(upload task)是否缓存response,需要缓存才调用
代理: didFinishDownloadingToURL
c
当下载结束时, 删除下载链接任务, 发送任务完成,并移除下载通知
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL
error:&error];
if (error) {
[[NSNotificationCenter defaultCenter]
postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNoti
fication object:downloadTask userInfo:error.userInfo];
}
代理: 返回字节大小信息
c
- (void)URLSession: downloadTask: didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:
(int64_t)totalBytesExpectedToWrite
返回收到的文件总字节数,是由Content-Length header提供,没有返回是
NSURLSessionTransferSizeUnknown
代理: 下载取消和暂停后恢复下载
c
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
if (self.downloadTaskDidResume) {
self.downloadTaskDidResume(session, downloadTask, fileOffset,
expectedTotalBytes);
}
}
当下载被取消或者失败后重新恢复下载时调用
如果一个正在下载任务被取消或者失败了,你可以请求一个resumeData对象
(比如在userInfo字典中通过NSURLSessionDownloadTaskResumeData这个
键来获取到resumeData)
并使用它来提供足够的信息以重新开始下载任务。
随后,你可以使用resumeData作为downloadTaskWithResumeData:或
downloadTaskWithResumeData:completionHandler:的参数。
当你调用这些方法时,你将开始一个新的下载任务。
一旦你继续下载任务,session会调用它的代理方法
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
其中的downloadTask参数表示的就是新的下载任务,这也意味着下载重新开始
了。
参数 fileOffset
当前已下载偏移量, 重新下载时从此处开始
为0重新下载
参数 expectedTotalBytes
表示文件总字节大小
数据解析
AFURLResponseSerialization
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding,
NSCopying>
@interface AFHTTPResponseSerializer : NSObject
<AFURLResponseSerialization>
@interface AFJSONResponseSerializer : AFHTTPResponseSerializer
@interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer
@interface AFXMLDocumentResponseSerializer :
AFHTTPResponseSerializer
@interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer
@interface AFImageResponseSerializer : AFHTTPResponseSerializer
@interface AFCompoundResponseSerializer : AFHTTPResponseSerializer
除了AFURL外,其他都遵循或继承AFHTTPResponseSerializer
AFURLResponseSerialization协议
AFURLResponseSerialization协议被一个对象采用,该对象将数据解码为更有
用的对象表示。 Response序列化器还可以对传入响应和数据执行验证
AFHTTPResponseSerializer
其他解析类的父类,他遵守上面的AFURLResponseSerialization协议
检查可接受的状态码和内容类型。 子类可能希望添加其他域特定的检查。
检查
validateResponse:(NSHTTPURLResponse *)response
AFJSONResponseSerializer
验证和解码JSON响应
接受MIME类型
application / json
text / json
text / javascript
其他大致原理差不多,解析并验证合法性
AFN中的UIKit
指示器的状态
管理者: AFNetworkActivityIndicatorManager
管理,创建加载提示的旋转小菊花
监听请求活动数, 如果 > 1 则转圈圈, 否则隐藏
对任务开始、暂停和完成的通知监听。在通知方法的实现上就是在主线程进行开
始或者停止动画
比如开始
[notificationCenter addObserver:self
selector:@selector(af_startAnimating)
name:AFNetworkingTaskDidResumeNotification object:task];
- (void)af_startAnimating {
dispatch_async(dispatch_get_main_queue(), ^{
[self.activityIndicatorView startAnimating];
});
}
AFImageDownloader
UIImageView+AFNetworking
完成图片下载,缓存,获取等功能
UIButton+AFNetworking分类
完成图片下载,缓存,获取等功能
UIProgressView+AFNetworking分类
主要实现了上传任务和下载任务与进度之间的绑定。
POST
上传视频
上传图片
上传音频