序言
AFNetworking2.0和3.0區(qū)別很大,也是因?yàn)樘O(píng)果廢棄了NSURLConnection,而改用了NSURLSession,AFNetworking3.0實(shí)際上只是對(duì)NSURLSession所做的操作進(jìn)行了高度封裝,提供更加簡(jiǎn)潔的API供編碼調(diào)用。
查看AFHTTPSessionManager.h文件,可知AFHTTPSessionManager是AFURLSessionManager的子類(lèi):
@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding, NSCopying>
- 實(shí)例代碼 - 下載一張圖片
// 下載圖片并且保存
- (void)downloadImgAndSave {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"image/jpeg", nil];
__weak __typeof__(self) weakSelf = self;
NSString *url = @"http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg";
// 開(kāi)始下載
[manager GET:url parameters:nil progress:^(NSProgress *downloadProgress) {
NSLog(@"progress:%lld",downloadProgress.completedUnitCount);
} success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"圖片下載完成");
__strong __typeof__(weakSelf) strongSelf = weakSelf;
strongSelf.imgView.image = [UIImage imageWithData:(NSData *)responseObject];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if (error) {
NSLog(@"%@",error.userInfo);
}
}];
}
運(yùn)行結(jié)果


一 GET請(qǐng)求調(diào)用函數(shù)分析
- AFHTTPSessionManager.m
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
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;
}
- AFURLSessionManager.m
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
二 AFN的GET和POST請(qǐng)求實(shí)現(xiàn)第二層 - AFURLSessionManager
- 實(shí)例代碼 - 下載一個(gè)較大文件
// 下載一個(gè)大文件
- (void)downloadBigFile {
// 1.創(chuàng)建配置參數(shù)
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
// 2.創(chuàng)建會(huì)話(huà)管理者
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
// 3.創(chuàng)建請(qǐng)求對(duì)象
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"]];
// 4.創(chuàng)建下載任務(wù)
/**
* 第一個(gè)參數(shù) - request:請(qǐng)求對(duì)象
* 第二個(gè)參數(shù) - progress:下載進(jìn)度block
* 其中: downloadProgress.completedUnitCount:已經(jīng)完成的大小
* downloadProgress.totalUnitCount:文件的總大小
* 第三個(gè)參數(shù) - destination:自動(dòng)完成文件剪切操作
* 其中: 返回值:該文件應(yīng)該被剪切到哪里
* targetPath:臨時(shí)路徑 tmp NSURL
* response:響應(yīng)頭
* 第四個(gè)參數(shù) - completionHandler:下載完成回調(diào)
* 其中: filePath:真實(shí)路徑 == 第三個(gè)參數(shù)的返回值
* error:錯(cuò)誤信息
*/
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress) {
__weak typeof(self) weakSelf = self;
// 獲取主線程,不然無(wú)法正確顯示進(jìn)度。
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperationWithBlock:^{
weakSelf.progressLbe.text = [NSString stringWithFormat:@"當(dāng)前下載進(jìn)度:%.2f%%",100.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount];
}];
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSURL *path = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [path URLByAppendingPathComponent:response.suggestedFilename];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
NSLog(@"File downloaded to: %@", filePath);
}];
// 5.開(kāi)啟下載任務(wù)
[downloadTask resume];
}
運(yùn)行結(jié)果


內(nèi)部封裝分析 AFURLSessionManager.m
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithRequest:request];
});
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
其中self.session是NSURLSession的一個(gè)實(shí)例對(duì)象,dataTaskWithRequest:是其創(chuàng)建任務(wù)的一個(gè)方式。
@property (readonly, nonatomic, strong) NSURLSession *session;
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
添加代理的封裝
- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
if (destination) {
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
}
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:downloadTask];
delegate.downloadProgressBlock = downloadProgressBlock;
}
其中setDelegate:forTask的實(shí)現(xiàn)
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
其中mutableTaskDelegatesKeyedByTaskIdentifier是一個(gè)可變字典
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier;
被調(diào)用的地方在
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}
進(jìn)而調(diào)用的是系統(tǒng)的代理
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (self.downloadTaskDidFinishDownloading) {
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
在系統(tǒng)
NSURLSessionDelegate回調(diào)方法中,下面方法被調(diào)用多次,即將任務(wù)作為key,從字典中取出對(duì)應(yīng)的delegate,再回調(diào)出去,最終回調(diào)到應(yīng)用層面。
我們看看AFURLSessionManager的方法目錄結(jié)構(gòu)就知道了
- 下圖是
NSURLSessionDelegate方法目錄結(jié)構(gòu)

- 下圖是
AFURLSessionDelegate方法目錄結(jié)構(gòu),和系統(tǒng)的NSURLSessionDelegate很像,別弄混了。

三 dataTaskWithRequest:completionHandler:函數(shù)詳解
說(shuō)明:這個(gè)NSURLSession的API容易跟AFURLSessionManager的API混淆,參數(shù)都是一個(gè)request和一個(gè)handler block。
- NSURLSession的API是這樣的
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
- 而AFURLSessionManager的API是這樣的
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError * error))completionHandler DEPRECATED_ATTRIBUTE;
- 實(shí)例代碼
// 下載任務(wù)執(zhí)行塊
- (void)dataTaskWithRequest {
// 下載地址
NSURL *url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.0.dmg"];
// 創(chuàng)建request請(qǐng)求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 設(shè)置HTTP請(qǐng)求頭中的Range
NSString *range = [NSString stringWithFormat:@"bytes=%zd-", self.currentLength];
[request setValue:range forHTTPHeaderField:@"Range"];
// 開(kāi)始下載任務(wù)
__weak typeof(self) weakSelf = self;
NSURLSessionDataTask *downloadTask = [self.manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
// 下載完成
// 清空長(zhǎng)度
weakSelf.currentLength = 0;
weakSelf.fileLength = 0;
// 關(guān)閉fileHandle
[weakSelf.fileHandle closeFile];
weakSelf.fileHandle = nil;
}];
}
其中manager是懶加載得到的
- (AFURLSessionManager *)manager {
if (_manager == nil) {
_manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
}
return _manager;
}
- AF內(nèi)部封裝實(shí)現(xiàn)
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
}
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
四 總結(jié)
AF請(qǐng)求操作內(nèi)部實(shí)現(xiàn)也是和原生NSURLSession操作一樣,創(chuàng)建task,調(diào)用resume發(fā)送請(qǐng)求。