AFURLSessionManager

本篇是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_SERIALNULL则表示队列为串行队列。如果为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机制监听进度的。

点击了解更多NSProgress(English)

点击了解更多NSProgress(中文)

- (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;

可以把数据写到一个我们定义的临时的地方

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容