概述
上一篇主要分析了基于NSURLConnection的AFURLConnectionOperation细移,本篇主要分析一下基于NSURLSession方式的相關(guān)代碼,主要分為核心類AFURLSessionManager及其子類AFHTTPSessionManager爽茴。
初始化
AFHTTPSessionManager封裝了HTTP請求的幾個方法寒矿,例如GET蹋半、POST等請求鹦聪,首先通過初始化方法創(chuàng)建AFHTTPSessionManager對象账阻。
初始化過程最終執(zhí)行到-(instance)initWithBaseURL: sessionConfiguration:方法中,下面是代碼注釋:
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url; //設(shè)置baseURL泽本,作為構(gòu)建url的前綴
self.requestSerializer = [AFHTTPRequestSerializer serializer];//序列化類
self.responseSerializer = [AFJSONResponseSerializer serializer];//反序列化類
return self;
}
首先調(diào)用父類對象的initWithSessionConfiguration:方法淘太,初始化一些基本參數(shù)。下面是部分代碼注釋:
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) { //創(chuàng)建默認(rèn)的NSURLSessionConfiguration對象
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1; //最大并發(fā)數(shù)為1规丽,同步隊列
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; //創(chuàng)建session對象
self.responseSerializer = [AFJSONResponseSerializer serializer]; //JSON序列化解析類
self.securityPolicy = [AFSecurityPolicy defaultPolicy]; //默認(rèn)安全策略類
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; //網(wǎng)絡(luò)狀態(tài)監(jiān)聽類
#endif
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init]; //線程鎖
self.lock.name = AFURLSessionManagerLockName;
...
return self;
}
首先創(chuàng)建一個NSURLSessionConfiguration對象蒲牧,該對象有三種創(chuàng)建方式,采用默認(rèn)的方式創(chuàng)建赌莺,該對象是屬于會話配置項(xiàng)造成,作用它創(chuàng)建的所有NSURLSession對象。然后創(chuàng)建根據(jù)sessionConfiguration對象創(chuàng)建一個session對象雄嚣,指定delegate和delegate執(zhí)行的queue晒屎,默認(rèn)是同步隊列。然后創(chuàng)建JSON反序列化對象和安全策略對象缓升,網(wǎng)絡(luò)狀態(tài)監(jiān)聽對象鼓鲁。mutableTaskDelegatesKeyedByTaskIdentifier字典保存sessionTask和sessionTaskDelegate的關(guān)系。同時設(shè)置線程鎖類控制多線程的數(shù)據(jù)同步港谊。
接下來在子類AFHTTPSessionManager的方法中設(shè)置baseURL骇吭,可以作為后續(xù)構(gòu)建Request對象url的前綴,然后設(shè)置序列化和反序列化的工具類歧寺。
NSURLSessionTask
AFHTTPSessionManager提供了一系列HTTP請求的方法燥狰,例如GET、POST斜筐、HEAD龙致、DELETE等方法,這些方法的流程相似顷链,都是先根據(jù)session對象創(chuàng)建一個task對象目代,然后調(diào)用resume方法啟動task。task對象有三種類型嗤练,分別是NSURLSessionDataTask榛了、NSURLSessionUploadTask和NSURLSessionDownloadTask,分別如下:
NSURLSessionDataTask
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
//創(chuàng)建request對象煞抬,序列化請求報文
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
//創(chuàng)建失敗霜大,拋給上層
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
//創(chuàng)建task
dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
首先構(gòu)建http請求的報文,包括請求頭革答、請求體等信息战坤,創(chuàng)建request對象遮婶,然后調(diào)用dataTaskWithRequest: completionHandler:方法創(chuàng)建NSURLSessionDataTask對象,方法如下:
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionDataTask *dataTask = nil;
dispatch_sync(url_session_manager_creation_queue(), ^{
dataTask = [self.session dataTaskWithRequest:request]; //創(chuàng)建task對象
});
[self addDelegateForDataTask:dataTask completionHandler:completionHandler]; //創(chuàng)建delegate湖笨,綁定completionHandler回調(diào)旗扑,關(guān)聯(lián)delegate
return dataTask;
}
首先在串行隊列中創(chuàng)建一個task對象,使用dispatch_sync保證線程阻塞直到創(chuàng)建完成慈省,為什么不直接在主線程做不太清楚臀防,然后調(diào)用addDelegateForDataTask方法為task對象添加delegate對象,并綁定completionHandler回調(diào)block边败,用于http請求完成后執(zhí)行袱衷。addDelegateForDataTask方法注釋如下:
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //創(chuàng)建delegate對象
delegate.manager = self;
delegate.completionHandler = completionHandler; //綁定completionHandler
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask]; //關(guān)聯(lián)task個delegate
}
首先創(chuàng)建一個delegate對象,綁定completionHandler笑窜,然后調(diào)用setDelegate:forTask:方法關(guān)聯(lián)task和delegate致燥,代碼注釋如下,
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate; //存入字典
[delegate setupProgressForTask:task]; //delegate監(jiān)聽task的進(jìn)度
[self addNotificationObserverForTask:task]; //添加通知
[self.lock unlock];
}
通過mutableTaskDelegatesKeyedByTaskIdentifier字典維護(hù)關(guān)系排截,key是task的taskIdentifier嫌蚤,value是delegate,通過setupProgressForTask方法使delegate監(jiān)聽task的進(jìn)度断傲。
NSURLSessionUploadTask
當(dāng)需要上傳一個數(shù)據(jù)到服務(wù)端脱吱,需要創(chuàng)建NSURLSessionUploadTask,創(chuàng)建方式有三種:
uploadTask = [self.session uploadTaskWithRequest:request fromFile:fileURL];
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData];
uploadTask = [self.session uploadTaskWithStreamedRequest:request];
支持從url地址认罩、內(nèi)存箱蝠、流中讀取數(shù)據(jù),創(chuàng)建task的流程和NSURLSessionDataTask類似垦垂,創(chuàng)建完成后需要為其添加一個delegate對象并綁定宦搬,如下:
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(NSProgress * __autoreleasing *)progress
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *uploadTask = nil;
dispatch_sync(url_session_manager_creation_queue(), ^{
uploadTask = [self.session uploadTaskWithRequest:request fromData:bodyData]; //創(chuàng)建一個uploadTask對象
});
[self addDelegateForUploadTask:uploadTask progress:progress completionHandler:completionHandler]; //添加delegate
return uploadTask;
}
addDelegateForUploadTask:progress:completionHandler:方法為uploadTask設(shè)置delegate,代碼注釋如下:
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init]; //創(chuàng)建delegate對象
delegate.manager = self;
delegate.completionHandler = completionHandler;
uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:uploadTask]; //綁定delegate和uploadTask對象
delegate.uploadProgressBlock = uploadProgressBlock; //設(shè)置上傳進(jìn)度回調(diào)blcok
}
首先創(chuàng)建delegate對象劫拗,然后綁定delegate和uploadTask對象间校,最后設(shè)置uploadProgressBlock,用于通知外界上傳數(shù)據(jù)的進(jìn)度杨幼。
NSURLSessionDownloadTask
當(dāng)需要下載數(shù)據(jù)并寫入指定地址時撇簿,需要創(chuàng)建NSURLSessionDownloadTask對象:
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(NSProgress * __autoreleasing *)progress
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
dispatch_sync(url_session_manager_creation_queue(), ^{
downloadTask = [self.session downloadTaskWithRequest:request]; //創(chuàng)建一個NSURLSessionDownloadTask
});
[self addDelegateForDownloadTask:downloadTask progress:progress destination:destination completionHandler:completionHandler]; //創(chuàng)建delegate,綁定
return downloadTask;
}
其中destination是一個block差购,返回一個url,表示需要寫入數(shù)據(jù)的地址汉嗽。addDelegateForDownloadTask方法和upload的類似欲逃,代碼注釋如下:
- (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] init]; //創(chuàng)建delegate對象
delegate.manager = self;
delegate.completionHandler = completionHandler; //綁定completionHandler
if (destination) {
//設(shè)置block,返回寫入下載數(shù)據(jù)的地址
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和downloadTask對象
delegate.downloadProgressBlock = downloadProgressBlock; //設(shè)置下載進(jìn)度回調(diào)blcok
}
AFURLSessionManagerTaskDelegate
AFURLSessionManagerTaskDelegate是代理對象饼暑,主要作用有兩個稳析,一是監(jiān)聽task的進(jìn)度洗做,并回拋給上層,二是處理task的回調(diào)方法彰居,delegate和task是一一對應(yīng)關(guān)系诚纸。下面是類的定義:
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData; //響應(yīng)數(shù)據(jù)
@property (nonatomic, strong) NSProgress *uploadProgress; //上傳進(jìn)度
@property (nonatomic, strong) NSProgress *downloadProgress; //下載進(jìn)度
@property (nonatomic, copy) NSURL *downloadFileURL; //下載地址
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; //下載完成回調(diào)
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; //上傳進(jìn)度block
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; //下載進(jìn)度blcok
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; //請求完成block
@end
mutableData負(fù)責(zé)接收和拼裝server響應(yīng)的數(shù)據(jù),uploadProgress和downloadProgress負(fù)責(zé)管理發(fā)送和接收數(shù)據(jù)的進(jìn)度陈惰,uploadProgressBlock和downloadProgressBlock負(fù)責(zé)將進(jìn)度回拋給上層畦徘。completionHandler是請求完成時調(diào)用的blcok,負(fù)責(zé)將響應(yīng)數(shù)據(jù)回拋給上層抬闯。downloadFileURL是存儲下載數(shù)據(jù)的本地地址井辆,通過執(zhí)行downloadTaskDidFinishDownloading回調(diào)得到。
-
監(jiān)聽進(jìn)度
調(diào)用setupProgressForTask:方法關(guān)聯(lián)task的進(jìn)度屬性溶握,注釋如下:
- (void)setupProgressForTask:(NSURLSessionTask *)task { __weak __typeof__(task) weakTask = task; //1.task需要傳輸?shù)乃袛?shù)據(jù)大小 self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend; self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive; //2.關(guān)聯(lián)progress和task的狀態(tài) [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]; }]; } //3.監(jiān)聽task的數(shù)據(jù)傳輸狀態(tài) [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived)) options:NSKeyValueObservingOptionNew context:NULL]; [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive)) options:NSKeyValueObservingOptionNew context:NULL]; [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent)) options:NSKeyValueObservingOptionNew context:NULL]; [task addObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend)) options:NSKeyValueObservingOptionNew context:NULL]; //4.監(jiān)聽progress的fractionCompleted屬性值 [self.downloadProgress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL]; [self.uploadProgress addObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted)) options:NSKeyValueObservingOptionNew context:NULL]; }
該方法首先獲取當(dāng)前task需要發(fā)送或者接受的數(shù)據(jù)總大小杯缺。然后關(guān)聯(lián)progress和task的狀態(tài),當(dāng)progress暫停時睡榆,相應(yīng)的task需要suspend萍肆,當(dāng)progress取消時,task會cancel胀屿,當(dāng)progress啟動時匾鸥,task會resume。然后通過kvo的方式監(jiān)聽task的進(jìn)度屬性碉纳,countOfBytesReceived和countOfBytesSent表示當(dāng)前的接受和發(fā)送的數(shù)據(jù)量勿负,countOfBytesExpectedToReceive和countOfBytesExpectedToSend表示需要接受和發(fā)送的數(shù)據(jù)總量。同時監(jiān)聽progress的fractionCompleted屬性劳曹,fractionCompleted屬性表示任務(wù)的整體進(jìn)度奴愉,值從0到1,kvo的響應(yīng)方法注釋如下:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { if ([object isKindOfClass:[NSURLSessionTask class]]) { if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) { self.downloadProgress.completedUnitCount = [change[@"new"] longLongValue]; //當(dāng)前接受的數(shù)據(jù)量 } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) { self.downloadProgress.totalUnitCount = [change[@"new"] longLongValue]; //需要接受的數(shù)據(jù)總量 } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) { self.uploadProgress.completedUnitCount = [change[@"new"] longLongValue]; //當(dāng)前發(fā)送的數(shù)據(jù)量 } else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) { self.uploadProgress.totalUnitCount = [change[@"new"] longLongValue]; //需要發(fā)送的數(shù)據(jù)總量 } } else if ([object isEqual:self.downloadProgress]) { if (self.downloadProgressBlock) { self.downloadProgressBlock(object); //進(jìn)度回拋給上層 } } else if ([object isEqual:self.uploadProgress]) { if (self.uploadProgressBlock) { self.uploadProgressBlock(object); //進(jìn)度回拋給上層 } } }
該方法根據(jù)監(jiān)聽到的屬性铁孵,實(shí)時更新progress的進(jìn)度屬性锭硼,然后將屬性回拋給上層。delegate調(diào)用cleanUpProgressForTask注銷kvo蜕劝。
-
處理URLSessionTask回調(diào)
在初始化的方法時檀头,通過[NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]將AFURLSessionManager對象設(shè)置為session的delegate,通過URLSession創(chuàng)建出來的每個task岖沛,在執(zhí)行的過程中暑始,即網(wǎng)絡(luò)請求的過程中,都會調(diào)用AFURLSessionManager對象來處理一系列代理方法婴削。然后AFURLSessionManager對象進(jìn)一步調(diào)用AFURLSessionManagerTaskDelegate對象來處理廊镜,具體實(shí)現(xiàn)在下文分析。
AFURLSessionManager
AFURLSessionManager是處理網(wǎng)絡(luò)請求回調(diào)的核心類唉俗,主要負(fù)責(zé)處理NSURLSession以及NSURLSessionTask的各種回調(diào)邏輯嗤朴。下面分析一下主要的delegate方法:
NSURLSessionDelegate
-
-(void)URLSession: didReceiveChallenge: completionHandler方法
發(fā)HTTPS請求時配椭,在SSL握手過程中,服務(wù)端將證書下發(fā)客戶端雹姊,客戶端需要校驗(yàn)服務(wù)端證書股缸,在AF代碼中,核心代碼注釋如下:
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { //如果需要驗(yàn)證trust對象 if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) { //用securityPolicy對象驗(yàn)證trust對象 credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; //驗(yàn)證通過吱雏,根據(jù)trust對象生成證書憑證 if (credential) { disposition = NSURLSessionAuthChallengeUseCredential; //使用憑證 } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; //默認(rèn)方式 } } else { disposition = NSURLSessionAuthChallengeRejectProtectionSpace; //驗(yàn)證失敗敦姻,終止 } } else { disposition = NSURLSessionAuthChallengePerformDefaultHandling; //用默認(rèn)方式 } ... if (completionHandler) { completionHandler(disposition, credential); }
該方法首先判斷是否需要驗(yàn)證trust對象,trust是一個SecTrustRef類型的對象坎背,用于對服務(wù)器端傳來的X.509證書評估替劈,然后用securityPolicy進(jìn)行驗(yàn)證。如果驗(yàn)證通過得滤,則用trust對象生成一個證書憑證陨献,并采用NSURLSessionAuthChallengeUseCredential的方式。如果不能生成憑證懂更,則用默認(rèn)方式眨业。最終將方式和憑證通過completionHandler的方式回調(diào)給系統(tǒng),進(jìn)行后續(xù)的校驗(yàn)和連接沮协。
NSURLSessionTaskDelegate
-
-(void)NSURLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:方法
該方法在服務(wù)端重定向一個url的時候會觸發(fā)龄捡,處理代碼注釋如下:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest *))completionHandler { NSURLRequest *redirectRequest = request; //用默認(rèn)重定向的request if (self.taskWillPerformHTTPRedirection) { //如果有自定義block,則用block返回的request對象 redirectRequest = self.taskWillPerformHTTPRedirection(session, task, response, request); } if (completionHandler) { //回調(diào)給系統(tǒng) completionHandler(redirectRequest); } }
-
-(void)URLSession:task:didReceiveChallenge:completionHandler:方法
該方法的邏輯和URLSessionDelegate的方法相同慷暂,但是task級別的方法聘殖,同時在兩個方法都實(shí)現(xiàn)的情況下,會優(yōu)先進(jìn)入上文方法進(jìn)行校驗(yàn)行瑞,不進(jìn)入該方法校驗(yàn)奸腺。
-
-(void)URLSession:task:needNewBodyStream方法
如果通過stream的方式上傳數(shù)據(jù)至服務(wù)器,會調(diào)用[self.session uploadTaskWithStreamedRequest:request]方法創(chuàng)建NSURLSessionUploadTask血久,在執(zhí)行task發(fā)送請求時突照,會觸發(fā)該方法,代碼注釋如下:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task needNewBodyStream:(void (^)(NSInputStream *bodyStream))completionHandler { NSInputStream *inputStream = nil; if (self.taskNeedNewBodyStream) { //自定義block處理氧吐,返回新的stream inputStream = self.taskNeedNewBodyStream(session, task); } else if (task.originalRequest.HTTPBodyStream && [task.originalRequest.HTTPBodyStream conformsToProtocol:@protocol(NSCopying)]) { inputStream = [task.originalRequest.HTTPBodyStream copy];//默認(rèn)的字節(jié)流 } if (completionHandler) { //回調(diào)給系統(tǒng) completionHandler(inputStream); } }
-
-(void)URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
當(dāng)發(fā)送請求報文時讹蘑,觸發(fā)該方法,返回發(fā)送的數(shù)據(jù)量等信息筑舅。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { int64_t totalUnitCount = totalBytesExpectedToSend; if(totalUnitCount == NSURLSessionTransferSizeUnknown) { NSString *contentLength = [task.originalRequest valueForHTTPHeaderField:@"Content-Length"]; if(contentLength) { //用contentLength替換totalBytesExpectedToSend totalUnitCount = (int64_t) [contentLength longLongValue]; } } //回調(diào)給上層座慰,包括當(dāng)前一次發(fā)送的數(shù)據(jù)、發(fā)送數(shù)據(jù)的數(shù)據(jù)總量豁翎、需要發(fā)送的數(shù)據(jù)量 if (self.taskDidSendBodyData) { self.taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalUnitCount); } }
bytesSent表示當(dāng)前一次發(fā)送的數(shù)據(jù)量角骤,totalBytesSent表示已經(jīng)發(fā)送的數(shù)據(jù)總量,totalUnitCount表示本次請求需要發(fā)送的數(shù)據(jù)總量心剥。
-
-(void)URLSession:task:didCompleteWithError:
當(dāng)前task完成時邦尊,會觸發(fā)該回調(diào)方法,NSURLSessionDataTask优烧、NSURLSessionDownloadTask蝉揍、NSURLSessionUploadTask結(jié)束時都會調(diào)用該方法,代碼注釋如下:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task]; if (delegate) { //調(diào)用delegate處理回調(diào) [delegate URLSession:session task:task didCompleteWithError:error]; [self removeDelegateForTask:task]; //刪除task對應(yīng)的delegate } if (self.taskDidComplete) { //如果有回調(diào)block畦娄,執(zhí)行block self.taskDidComplete(session, task, error); } }
首先通過task的taskIdentifier找到對應(yīng)的delegate又沾,然后調(diào)用delegate的方法處理,最后再刪除delegate熙卡,如果有回調(diào)block杖刷,執(zhí)行block。下面是delegate對象方法的注釋:
- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { ... //data是響應(yīng)報文數(shù)據(jù) NSData *data = nil; if (self.mutableData) { data = [self.mutableData copy]; //We no longer need the reference, so nil it out to gain back some memory. self.mutableData = nil; } ... if (error) { //如果請求失敗 userInfo[AFNetworkingTaskDidCompleteErrorKey] = error; dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{ if (self.completionHandler) { //在指定隊列或者主隊列中將error回調(diào)給上層 self.completionHandler(task.response, responseObject, error); } ... }); }); } else { dispatch_async(url_session_manager_processing_queue(), ^{ NSError *serializationError = nil; //反序列化響應(yīng)報文數(shù)據(jù) responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError]; if (self.downloadFileURL) { //如果是downloadTask驳癌,responseObject是downloadFileURL responseObject = self.downloadFileURL; } ... 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); //在指定隊列或者主隊列中將反序列后的對象responseObject回調(diào)給上層 } }); ... }); } #pragma clang diagnostic pop }
該方法主要是獲取到服務(wù)端響應(yīng)報文數(shù)據(jù)后滑燃,用responseSerializer反序列化數(shù)據(jù),并回調(diào)給上層颓鲜,當(dāng)創(chuàng)建一個NSURLSessionDataTask類型的task并執(zhí)行時表窘,會得到報文數(shù)據(jù)mutableData。如果創(chuàng)建NSURLSessionDownloadTask執(zhí)行甜滨,直接下載數(shù)據(jù)到本地乐严,mutableData為nil。反序列化的過程在異步隊列中執(zhí)行衣摩,提高了性能昂验。
NSURLSessionDataDelegate
使用NSURLSessionDataTask發(fā)HTTP請求時,在請求的過程中艾扮,會觸發(fā)以下代理方法:
-
-(void)URLSession:dataTask:didReceiveResponse:completionHandler:方法
當(dāng)開始接收到服務(wù)器的響應(yīng)時既琴,該方法被調(diào)用,代碼注釋如下:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { //默認(rèn)處理方式 NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow; if (self.dataTaskDidReceiveResponse) { //如果實(shí)現(xiàn)block栏渺,通過block修改處理方式 disposition = self.dataTaskDidReceiveResponse(session, dataTask, response); } if (completionHandler) { //回調(diào)給系統(tǒng) completionHandler(disposition); } }
當(dāng)接收到服務(wù)器響應(yīng)時呛梆,說明網(wǎng)絡(luò)請求已經(jīng)連接,默認(rèn)是NSURLSessionResponseAllow表示允許task繼續(xù)執(zhí)行磕诊,進(jìn)行通信和數(shù)據(jù)傳輸填物,如果外部設(shè)置了dataTaskDidReceiveResponse的block,則可以修改通過block修改處理方式霎终。其他的處理方式有:
NSURLSessionResponseCancel //取消task
NSURLSessionResponseAllow //允許task繼續(xù)執(zhí)行
NSURLSessionResponseBecomeDownload //dataTask變成downloadTask
NSURLSessionResponseBecomeStream //變成NSURLSessionStreamTask
-
-(void)URLSession:dataTask:didBecomeDownloadTask:
上一個方法的disposition設(shè)置為NSURLSessionResponseBecomeDownload時滞磺,dataTask變成了downloadTask,會觸發(fā)該方法莱褒,需要對將之前delegate關(guān)聯(lián)到新的task击困。代碼注釋如下:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; if (delegate) { //重新關(guān)聯(lián)delegate和task [self removeDelegateForTask:dataTask]; [self setDelegate:delegate forTask:downloadTask]; } if (self.dataTaskDidBecomeDownloadTask) { self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask); } }
-
-(void)URLSession:dataTask:didReceiveData:
當(dāng)接收服務(wù)端響應(yīng)后并開始傳輸數(shù)據(jù)時,該方法一次或者多次被出發(fā),返回服務(wù)端響應(yīng)的報文數(shù)據(jù)阅茶,在該方法中調(diào)用delegate拼接報文數(shù)據(jù)蛛枚。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask]; //delegate處理data數(shù)據(jù) [delegate URLSession:session dataTask:dataTask didReceiveData:data]; if (self.dataTaskDidReceiveData) { self.dataTaskDidReceiveData(session, dataTask, data); } }
AFURLSessionManagerTaskDelegate對象負(fù)責(zé)處理響應(yīng)報文數(shù)據(jù)data:
- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [self.mutableData appendData:data]; //拼接到mutableData中 }
delegate將data拼接到mutableData中。
-
-(void)URLSession:dataTask:willCacheResponse:completionHandler:方法
當(dāng)構(gòu)建一個request對象時脸哀,默認(rèn)的緩存屬性策略cachePolicy是NSURLRequestUseProtocolCachePolicy蹦浦,即根據(jù)服務(wù)端響應(yīng)報文的header中的緩存策略決定是否緩存數(shù)據(jù),當(dāng)需要緩存數(shù)據(jù)時撞蜂,會觸發(fā)該方法盲镶,將響應(yīng)報文數(shù)據(jù)緩存到本地,實(shí)現(xiàn)該方法可以修改需要緩存的數(shù)據(jù)蝌诡。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { NSCachedURLResponse *cachedResponse = proposedResponse; if (self.dataTaskWillCacheResponse) { //如果實(shí)現(xiàn)block溉贿,通過block修改緩存數(shù)據(jù) cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse); } if (completionHandler) { //回調(diào)給系統(tǒng),存儲緩存數(shù)據(jù) completionHandler(cachedResponse); } }
NSURLSessionDownloadDelegate
-
URLSession:downloadTask:didFinishDownloadingToURL:
執(zhí)行NSURLSessionDownloadTask下載數(shù)據(jù)到本地浦旱,當(dāng)下載完成時宇色,會觸發(fā)該方法,代碼注釋如下:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask]; if (self.downloadTaskDidFinishDownloading) { //如果實(shí)現(xiàn)block闽寡,執(zhí)行下面邏輯 NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); //獲取需要寫入數(shù)據(jù)的地址fileURL if (fileURL) { delegate.downloadFileURL = fileURL; NSError *error = nil; [[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error]; //將寫入location地址的數(shù)據(jù)移至fileURL if (error) { [[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo]; } return; } } if (delegate) { //通過delegate處理下載完成的邏輯 [delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location]; } }
如果實(shí)現(xiàn)了downloadTaskDidFinishDownloading的block代兵,執(zhí)行block提供寫入下載數(shù)據(jù)的地址fileURL,因?yàn)橹跋到y(tǒng)默認(rèn)將下載數(shù)據(jù)寫入location地址爷狈,會將location的數(shù)據(jù)移至fileURL箩艺。如果沒有實(shí)現(xiàn)block就缆,默認(rèn)調(diào)用delegate的方法處理邏輯环壤,代碼注釋如下:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { ... if (self.downloadTaskDidFinishDownloading) { self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location); //獲取下載地址downloadFileURL if (self.downloadFileURL) {//將location中的數(shù)據(jù)移至downloadFileURL [[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError]; ... } } }
該方法執(zhí)行downloadTaskDidFinishDownloading獲取需要寫入下載數(shù)據(jù)的地址fileURL一死,在之前的addDelegateForDownloadTask方法中設(shè)置了downloadTaskDidFinishDownloading。然后將默認(rèn)location地址中的下載數(shù)據(jù)移動至fileURL羡微。
-
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
執(zhí)行NSURLSessionDownloadTask下載數(shù)據(jù)的過程中谷饿,會觸發(fā)該方法通知下載的進(jìn)度,代碼注釋如下:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { if (self.downloadTaskDidWriteData) { //將進(jìn)度數(shù)據(jù)回調(diào)給外界 self.downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); } }
bytesWritten是本次接收到的數(shù)據(jù)妈倔,totalBytesWritten是已經(jīng)接受到數(shù)據(jù)字節(jié)數(shù)博投,totalBytesExpectedToWrite是需要下載的總數(shù)據(jù)量。
總結(jié)
AFURLSessionManager實(shí)現(xiàn)了NSURLSession盯蝴、NSURLSessionTask及其子類的各種代理回調(diào)方法毅哗,同時提供了各種自定義block,增強(qiáng)了代碼邏輯的靈活性捧挺,同時可以監(jiān)聽數(shù)據(jù)傳輸?shù)倪M(jìn)度虑绵,通知外界。該模塊對于網(wǎng)絡(luò)請求的邏輯封裝的比較好闽烙,相關(guān)代碼值得學(xué)習(xí)翅睛。