本篇是AFNetworking 3.0源码解读的第五篇了。
AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager
AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy
AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization
AFNetworking 3.0 源码解读(四)之 AFURLResponseSerialization
这次主要介绍AFURLSessionManager这个类了。下一篇会介绍 AFHTTPSessionManager 。它是AFURLSessionManager的一个子类。
其实,AFURLSessionManager 创建并管理着NSURLSession这个对象。而NSURLSession又基于NSURLSessionConfiguration。
AFURLSessionManager实现了四个协议:
1.NSURLSessionDelegate
URLSession:didBecomeInvalidWithError:
URLSession:didReceiveChallenge:completionHandler:
URLSessionDidFinishEventsForBackgroundURLSession:
2. NSURLSessionTaskDelegate
URLSession:willPerformHTTPRedirection:newRequest:completionHandler:
URLSession:task:didReceiveChallenge:completionHandler:
URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
URLSession:task:needNewBodyStream:
URLSession:task:didCompleteWithError:
3. NSURLSessionDataDelegate
URLSession:dataTask:didReceiveResponse:completionHandler:
URLSession:dataTask:didBecomeDownloadTask:
URLSession:dataTask:didReceiveData:
URLSession:dataTask:willCacheResponse:completionHandler:
4. NSURLSessionDownloadDelegate
URLSession:downloadTask:didFinishDownloadingToURL:
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
上边的这些协议方法在实现部分都会介绍到,如果自己写的子类中重写了这些代理方法,一定要调用[super xxx]。这篇也会很长,本来打算差分成两个篇幅的,先因为方法大都比较荣日理解,最终决定还是放在一篇中比较好理解。
我们对AFURLSessionManager的头文件做一个介绍:
1.@property(readonly,nonatomic,strong)NSURLSession*session;
关于NSURLSession的介绍,可以参考官方的文档,文档中提出,相对于NSURLConnection ,NSURLSession强大的功能是支持后台上传和下载。不过值得注意的是,这个对象与它的delegate之间的是一个强引用关系,因此在释放NSURLSession时,要做好处理。
在网上看到了这篇文章,使用NSURLSession,可以说大体的讲了NSURLSession的用法,不过我更喜欢开头的那首诗。
有的程序员老了,还没听过NSURLSession
有的程序员还嫩,没用过NSURLConnection
有的程序员很单纯,他只知道AFN.
2.@property(readonly,nonatomic,strong)NSOperationQueue*operationQueue;
为NSURLSession 绑定一个队列。并且设置这个队列的最大并发数maxConcurrentOperationCount为1.
3.@property(nonatomic,strong)id responseSerializer;
这是序列化响应数据的对象,默认的模式是AFJSONResponseSerializer,而且不能为空。
4.@property(nonatomic,strong) AFSecurityPolicy *securityPolicy;
安全策略,默认是defaultPolicy。
5.@property(readwrite,nonatomic,strong) AFNetworkReachabilityManager *reachabilityManager;
网络监控管理者。
跟获取会话任务相关的属性:
6.@property(readonly,nonatomic,strong)NSArray *tasks;
当前被管理的包括data upload download 的任务的集合
7.@property(readonly,nonatomic,strong)NSArray *dataTasks;
当前 data 的任务集合
8.@property(readonly,nonatomic,strong)NSArray *uploadTasks;
当前 upload 的任务集合
9.@property(readonly,nonatomic,strong)NSArray *downloadTasks;
当前 download 的任务集合。
回调的队列
10.@property(nonatomic,strong, nullable)dispatch_queue_tcompletionQueue;
请求成功后,回调block会在这个队列中调用,如果为空,就在主队列。
11.@property(nonatomic,strong, nullable) dispatch_group_t completionGroup;
请求成功后,回调block会在这个组中调用,如果为空,就使用一个私有的。
修复后台操作的bug
12.@property(nonatomic,assign)BOOLattemptsToRecreateUploadTasksForBackgroundSessions;
这个属性用来解决在后台创建上传任务返回nil的bug,默认为NO,如果设为YES,在后台创建上传任务失败会,会尝试重新创建该任务。
初始化相关
13.-(instancetype)initWithSessionConfiguration:(nullableNSURLSessionConfiguration*)configurationNS_DESIGNATED_INITIALIZER;
这个方法是指定的初始化方法。那么什么叫指定的呢?
NS_DESIGNATED_INITIALIZER
这个宏告诉开发者,如果写一个集成A类的子类B,那么就要调用父类A的制定的初始化方法。举个例子:
@interfaceMyClass:NSObject@property(copy,nonatomic)NSString*name;-(instancetype)initWithName:(NSString*)nameNS_DESIGNATED_INITIALIZER;-(instancetype)init;@end
如果我集成了MyClass而没有时间initWithName: 方法,就会收到一个警告信息。点击这里查看详细信息
14.-(void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
根据是否取消未完成的任务来是session失效。
NSURLSession有两个方法:
-(void)finishTasksAndInvalidate; 标示待完成所有的任务后失效
-(void)invalidateAndCancel; 标示 立即失效,未完成的任务也将结束
NSURLSessionDataTask
15.- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;16.- (NSURLSessionDataTask*)dataTaskWithRequest:(NSURLRequest*)request uploadProgress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock downloadProgress:(nullablevoid(^)(NSProgress*downloadProgress))downloadProgressBlock completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;
上边的这两个方法是和DataTask 相关的方法。
NSURLSessionUploadTask
17.- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request fromFile:(NSURL*)fileURL progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;18.- (NSURLSessionUploadTask*)uploadTaskWithRequest:(NSURLRequest*)request fromData:(nullableNSData*)bodyData progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;19.- (NSURLSessionUploadTask*)uploadTaskWithStreamedRequest:(NSURLRequest*)request progress:(nullablevoid(^)(NSProgress*uploadProgress))uploadProgressBlock completionHandler:(nullablevoid(^)(NSURLResponse*response,id_Nullable responseObject,NSError* _Nullable error))completionHandler;
上边的这三个方法是和UploadTask 相关的方法。分别对应fileURL/data/request 这三种不同的数据源。
NSURLSessionDownloadTask
20.- (NSURLSessionDownloadTask*)downloadTaskWithRequest:(NSURLRequest*)request progress:(nullablevoid(^)(NSProgress*downloadProgress))downloadProgressBlock destination:(nullableNSURL* (^)(NSURL*targetPath,NSURLResponse*response))destination completionHandler:(nullablevoid(^)(NSURLResponse*response,NSURL* _Nullable filePath,NSError* _Nullable error))completionHandler;21.- (NSURLSessionDownloadTask*)downloadTaskWithResumeData:(NSData*)resumeData progress:(nullablevoid(^)(NSProgress*downloadProgress))downloadProgressBlock destination:(nullableNSURL* (^)(NSURL*targetPath,NSURLResponse*response))destination completionHandler:(nullablevoid(^)(NSURLResponse*response,NSURL* _Nullable filePath,NSError* _Nullable error))completionHandler;
上边的这两个方法是和DownloadTask 相关的方法。
头文件中剩余的方法就是跟最开始给出的代理有关了,让我们能够通过Block来处理那些代理的事件。在这就不做介绍了。
在这里再罗列出使用这个类中用到的通知:
AFNetworkingTaskDidResumeNotification
AFNetworkingTaskDidCompleteNotification
AFNetworkingTaskDidSuspendNotification
AFURLSessionDidInvalidateNotification
AFURLSessionDownloadTaskDidFailToMoveFileNotification
AFNetworkingTaskDidCompleteResponseDataKey
AFNetworkingTaskDidCompleteSerializedResponseKey
AFNetworkingTaskDidCompleteResponseSerializerKey
AFNetworkingTaskDidCompleteAssetPathKey
AFNetworkingTaskDidCompleteErrorKey
** 通过Block和通知,我们就有能力接收到跟网络请求先关的事件和数据。也就是我们可以使用这些来处理我们的业务逻辑。**
我们现在来看.m文件的内容
#ifndefNSFoundationVersionNumber_iOS_8_0#defineNSFoundationVersionNumber_With_Fixed_5871104061079552_bug 1140.11#else#defineNSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0#endif
上边的这个宏的目的是通过NSFoundation的版本来判断当前ios版本,关键是这个宏的调试目标是IOS,来看看系统是怎么定义的:
那么我们就能够联想到,目前我们能够判断系统版本号的方法有几种呢?最少三种:
[UIDevice currentDevice].systemVersion
通过比较Foundation框架的版本号,iOS系统升级的同时Foundation框架的版本也会提高
通过在某系版本中新出现的方法来判断,UIAlertController 这个类是iOS8之后才出现的 NS_CLASS_AVAILABLE_IOS(8_0),如果当前系统版本没有这个类NSClassFromString(@"UIAlertController" == (null),从而判断当前版本是否大于等于iOS8
staticdispatch_queue_turl_session_manager_creation_queue() {staticdispatch_queue_taf_url_session_manager_creation_queue;staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{ af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL); });returnaf_url_session_manager_creation_queue;}
AFNetworking中所有的和创建任务相关的事件都放到了一个单例的队列中,我们平时可能会使用这些方法,但还是可能会忽略一些内容,dispatch_queue_create()这个是队列的方法,第一个参数是队列的identifier,第二个参数则表示这个队列是串行队列还是并行队列。
如果第二个参数为DISPATCH_QUEUE_SERIAL或NULL则表示队列为串行队列。如果为DISPATCH_QUEUE_CONCURRENT则表示是并行队列。
关于队列的小的知识点,参考了这篇文章:Objective C 高级进阶— GCD队列浅析(一).
staticvoidurl_session_manager_create_task_safely(dispatch_block_t block) {if(NSFoundationVersionNumber
再看这个方法,看名字能够知道这应该是一个安全创建人物的方法,那么我们会很疑惑,为什么创建人物要是安全的呢?难道我们按照顺序创建人物,根据各自的Block回调处理事件会有问题?? 是的,按照https://github.com/AFNetworking/AFNetworking/issues/2093这个的描述:
加入我们创建了一个人物task1对应completionHandler1,然后又创建了task2对应的completionHandler2,这时候在task2数据还没有返回的前提下,task1的数据返回了,就会调用completionHandler2,就是这样的一个bug,造成任务的创建是不安全的,不过这个问题已经在ios8后修复了。
这个方法还有一个小知识点:dispatch_block_t,点击去可以看到:
typedefvoid(^dispatch_block_t)(void);
关于这个Block我们应该注意几点:
非ARC情况下,Block被allocated或者copied到堆后,一定要记得释放它,通过[release]或者Block_release()
声明Block时,它是被分配到栈上的,要使用他,需要copy到堆才安全,因为栈内存是系统管理的,随时可能被释放。
staticdispatch_queue_turl_session_manager_processing_queue() {staticdispatch_queue_taf_url_session_manager_processing_queue;staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{ af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT); });returnaf_url_session_manager_processing_queue;}
这个方法是创建一个队列用来管理数据的处理。和上边的创建的方法对比,这个方法创建的队列是一个并行的队列,这就加快了数据的处理速度。
staticdispatch_group_turl_session_manager_completion_group(){staticdispatch_group_taf_url_session_manager_completion_group;staticdispatch_once_tonceToken; dispatch_once(&onceToken, ^{ af_url_session_manager_completion_group = dispatch_group_create(); });returnaf_url_session_manager_completion_group;}
typedefNSInputStream* (^AFURLSessionTaskNeedNewBodyStreamBlock)(NSURLSession*session,NSURLSessionTask*task);
大概讲一下这个的使用方法,其实这行代码的目的就是给一个Block定义一个名称,在AFNEtworking中后边的代码,在使用这个Block的时候,就这么使用
@property(readwrite,nonatomic,copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
AFURLSessionManagerTaskDelegate
这个代理对象的目的是:
处理上传或下载的进度
处理获取完数据后的行为
看这些属性,我们需要了解的是NSProgress这个类,这个类是apple为了管理进度在ios7新增的类。我们在ios开发中,但凡使用到跟进度相关的功能时,应尽量考虑始终它。它内部是使用kvo机制监听进度的。
- (instancetype)init {self= [superinit];if(!self) {returnnil; }self.mutableData = [NSMutableDatadata];self.uploadProgress = [[NSProgressalloc] initWithParent:niluserInfo:nil];self.uploadProgress.totalUnitCount =NSURLSessionTransferSizeUnknown;self.downloadProgress = [[NSProgressalloc] initWithParent:niluserInfo:nil];self.downloadProgress.totalUnitCount =NSURLSessionTransferSizeUnknown;returnself;}
来看看如何把task和进度绑定在一起
- (void)setupProgressForTask:(NSURLSessionTask*)task {__weak__typeof__(task) weakTask = task;// 设置进度的总单元数self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;// 设置上传为可取消的[self.uploadProgress setCancellable:YES];[self.uploadProgress setCancellationHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask cancel];}];// 设置上传为可暂停的[self.uploadProgress setPausable:YES];[self.uploadProgress setPausingHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask suspend];}];// 设置重新开始if([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) { [self.uploadProgress setResumingHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask resume]; }];}[self.downloadProgress setCancellable:YES];[self.downloadProgress setCancellationHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask cancel];}];[self.downloadProgress setPausable:YES];[self.downloadProgress setPausingHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask suspend];}];if([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) { [self.downloadProgress setResumingHandler:^{ __typeof__(weakTask) strongTask = weakTask; [strongTask resume]; }];}[task addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesReceived)) options:NSKeyValueObservingOptionNewcontext:NULL];[task addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive)) options:NSKeyValueObservingOptionNewcontext:NULL];[task addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesSent)) options:NSKeyValueObservingOptionNewcontext:NULL];[task addObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend)) options:NSKeyValueObservingOptionNewcontext:NULL];[self.downloadProgress addObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNewcontext:NULL];[self.uploadProgress addObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNewcontext:NULL];}
这个方法很长,但是也很简单,通过task.countOfBytesExpectedToSend能够获取到发送数据的总大小,通过task.countOfBytesExpectedToReceive能够获取到下载数据的总大小。
NSProgress 通过监听fractionCompleted这个属性来获取进度。
注意:在写监听方法的时候,这个options使用了NSKeyValueObservingOptionNew,代表什么意思呢?
点击去后看到是一个NSKeyValueObservingOptions的枚举:
NSKeyValueObservingOptionNew 把更改之前的值提供给处理方法
NSKeyValueObservingOptionOld 把更改之后的值提供给处理方法
NSKeyValueObservingOptionInitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值
NSKeyValueObservingOptionPrior 分2次调用。在值改变之前和值改变之后
--
// 取消监听- (void)cleanUpProgressForTask:(NSURLSessionTask*)task { [task removeObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))]; [task removeObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]; [task removeObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesSent))]; [task removeObserver:selfforKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]; [self.downloadProgress removeObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))]; [self.uploadProgress removeObserver:selfforKeyPath:NSStringFromSelector(@selector(fractionCompleted))];}
--
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context {if([object isKindOfClass:[NSURLSessionTaskclass]] || [object isKindOfClass:[NSURLSessionDownloadTaskclass]]) {if([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; }elseif([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue]; } }elseif([object isEqual:self.downloadProgress]) {if(self.downloadProgressBlock) {self.downloadProgressBlock(object); } }elseif([object isEqual:self.uploadProgress]) {if(self.uploadProgressBlock) {self.uploadProgressBlock(object); } }}
在这里要说一下关于task四个代理的调用问题。
task一共有4个delegate,只要设置了一个,就代表四个全部设置,有时候一些delegate不会被触发的原因在于这四种delegate是针对不同的URLSession类型和URLSessionTask类型来进行响应的,也就是说不同的类型只会触发这些delegate中的一部分,而不是触发所有的delegate。
举例说明如下
触发NSURLSessionDataDelegate
//使用函数dataTask来接收数据-(void)URLSession:(NSURLSession*)session dataTask:(NSURLSessionDataTask*)dataTask didReceiveData:(NSData*)data//则NSURLSession部分的代码如下NSURLSessionConfiguration* ephConfiguration=[NSURLSessionConfigurationdefaultSessionConfiguration];NSURLSession* session=[NSURLSessionsessionWithConfiguration:ephConfiguration delegate:selfdelegateQueue:[NSOperationQueuemainQueue]];NSURL* url=[NSURLURLWithString:@"http://www.example.com/external_links/01.png"];NSURLSessionDataTask* dataTask=[session dataTaskWithURL:url];[dataTask resume];
触发NSURLSessionDownloadDelegate
//使用函数downloadTask来接受数据-(void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTask didFinishDownloadingToURL:(NSURL*)location//则NSURLSession部分的代码如下NSURLSessionConfiguration* ephConfiguration=[NSURLSessionConfigurationdefaultSessionConfiguration];NSURLSession* session=[NSURLSessionsessionWithConfiguration:ephConfiguration delegate:selfdelegateQueue:[NSOperationQueuemainQueue]];NSURL* url=[NSURLURLWithString:@"http://www.example.com/external_links/01.png"];NSURLSessionDownloadTask* dataTask=[session downloadTaskWithURL:url];[dataTask resume];
这两段代码的主要区别在于NSURLSessionTask的类型的不同,造成了不同的Delegate被触发.
#pragma mark - NSURLSessionDataTaskDelegate- (void)URLSession:(__unusedNSURLSession*)session dataTask:(__unusedNSURLSessionDataTask*)dataTask didReceiveData:(NSData*)data{ [self.mutableData appendData:data];}
--
#pragma mark - NSURLSessionTaskDelegate- (void)URLSession:(__unusedNSURLSession*)session task:(NSURLSessionTask*)taskdidCompleteWithError:(NSError*)error{#pragma clang diagnostic push#pragma clang diagnostic ignored"-Wgnu"__strongAFURLSessionManager *manager =self.manager; __blockidresponseObject =nil;// 使用字典来存放请求的结果__blockNSMutableDictionary*userInfo = [NSMutableDictionarydictionary]; userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;//Performance Improvement from #2672NSData*data =nil;if(self.mutableData) { data = [self.mutableDatacopy];//We no longer need the reference, so nil it out to gain back some memory.self.mutableData =nil; }//判断是否有downloadFileURLif(self.downloadFileURL) { userInfo[AFNetworkingTaskDidCompleteAssetPathKey] =self.downloadFileURL; }elseif(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(), ^{ [[NSNotificationCenterdefaultCenter] 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(), ^{ [[NSNotificationCenterdefaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo]; }); }); }); }#pragma clang diagnostic pop}
这个方法是获取数据完成了方法。最终通过self.completionHandler和** [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];**这两个手段来传递数据和事件。
我们主要看看这个通知的userinfo会有那些信息:
AFNetworkingTaskDidCompleteResponseSerializerKey -> manager.responseSerializer
AFNetworkingTaskDidCompleteAssetPathKey -> self.downloadFileURL
AFNetworkingTaskDidCompleteResponseDataKey -> data
AFNetworkingTaskDidCompleteErrorKey -> error
AFNetworkingTaskDidCompleteSerializedResponseKey -> responseObject
--
#pragma mark - NSURLSessionDownloadTaskDelegate- (void)URLSession:(NSURLSession*)session downloadTask:(NSURLSessionDownloadTask*)downloadTaskdidFinishDownloadingToURL:(NSURL*)location{NSError*fileManagerError =nil;self.downloadFileURL =nil;if(self.downloadTaskDidFinishDownloading) {self.downloadFileURL =self.downloadTaskDidFinishDownloading(session, downloadTask, location);if(self.downloadFileURL) { [[NSFileManagerdefaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];if(fileManagerError) { [[NSNotificationCenterdefaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo]; } } }}
这个方法在下载完成后会调用。之前有一个使用场景,就是视频边下载边播放。要求在视频在下载完之前拿到正在下载的数据。ASI有一个属性能够拿到fileURL,AFNetworking却没有这个属性,现在看来,通过设置
(void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSessionsession, NSURLSessionDownloadTaskdownloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
可以把数据写到一个我们定义的临时的地方