iOS-AFNetworking之AFHTTPSessionManager詳解

序言

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é)果

image.png
image.png
一 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é)果

image.png
1.gif

內(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.sessionNSURLSession的一個(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)
image.png
  • 下圖是AFURLSessionDelegate方法目錄結(jié)構(gòu),和系統(tǒng)的NSURLSessionDelegate很像,別弄混了。
image.png
三 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)求。


項(xiàng)目連接地址 - AFNetworkingDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容