AFNetWoking(3.0)是一個著名的iOS第三方網(wǎng)絡(luò)通信框架厉亏,當(dāng)前最新的3.0版的實現(xiàn)完全基于NSURLSession進行了封裝,作為一個剛剛學(xué)習(xí)NSURLSession的小白,本文主要關(guān)注AFNetWorking(3.0)中使用NSURLSession進行網(wǎng)絡(luò)通信的邏輯。
NSURLSession是蘋果公司在iOS7.0后推出用來替代NSURLConnection進行網(wǎng)絡(luò)請求的API,有關(guān)NSURLSession的使用在我對官方文檔翻譯后總結(jié)的NSURLSession的使用中有介紹和說明楣嘁。
AFNetWorking(3.0)中進行網(wǎng)絡(luò)通信的類
在AFNetWorking(3.0)中,實現(xiàn)網(wǎng)絡(luò)通信功能的類主要有三個
- AFHTTPSessionManager
- AFURLSessionManager
- AFURLSessionManagerTaskDelegate
其中AFHTTPSessionManager是AFURLSessionManager的子類珍逸,它封裝了用戶對于HTTP請求參數(shù)的操作逐虚,用戶通過調(diào)用相關(guān)方法就能進行GET、POST谆膳、PUT等方法叭爱;
AFURLSessionManager是AFNetWorking(3.0)的核心,它封裝了幾乎所有與NSURLSession相關(guān)的方法漱病,并實現(xiàn)了NSURLSessionDelegate买雾、 NSURLSessionTaskDelegate,、NSURLSessionDataDelegate杨帽、NSURLSessionDownloadDelegate等委托的方法漓穿。
AFURLSessionManagerTaskDelegate也實現(xiàn)委托的方法,不過僅僅實現(xiàn)了部分睦尽,它的作用主要是管理上傳與下載任務(wù)的進度。
AFNetWorking(3.0)的網(wǎng)絡(luò)通信流程
1. 創(chuàng)建一個AFURLSessionManager對象
AFURLSessionManager封裝了關(guān)于NSURLSession對象的操作型雳,因此通過創(chuàng)建一個AFURLSessionManager對象就簡化了創(chuàng)建NSURLSession的流程当凡。
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
//1山害、config為空設(shè)為默認config
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
//2、創(chuàng)建操作隊列沿量,設(shè)置最大并發(fā)數(shù)
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
//3浪慌、創(chuàng)建session
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
//4、創(chuàng)建響應(yīng)序列化朴则,安全策略权纤,任務(wù)識別關(guān)鍵字
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//5、創(chuàng)建鎖
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//6乌妒、為session管理的所有任務(wù)設(shè)置關(guān)聯(lián)的AFURLSessionManagerTaskDelegate對象
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
//1汹想、數(shù)據(jù)任務(wù)
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
//2、上傳任務(wù)
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
//3撤蚊、下載任務(wù)
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
通過上述代碼可以看到AFURLSessionManager的初始化做了以下工作:
- 初始化會話配置古掏,默認的會話配置為defaultSessionConfiguration類型
- 初始化操作隊列,隊列并發(fā)數(shù)為1侦啸,即為串行隊列
- 通過會話配置和操作隊列創(chuàng)建NSURLSession對象槽唾,并將自身設(shè)為委托
- 創(chuàng)建網(wǎng)絡(luò)響應(yīng)序列化工具對象
- 定義了網(wǎng)絡(luò)通信的安全策略
- 初始化映射數(shù)組,在進行多個網(wǎng)絡(luò)請求任務(wù)時光涂,每個task都對應(yīng)著一個AFURLSessionManagerTaskDelegate對象
- 為session管理的所有任務(wù)設(shè)置關(guān)聯(lián)的AFURLSessionManagerTaskDelegate對象
2. 封裝網(wǎng)絡(luò)通信請求參數(shù)
有了NSURLSession對象之后庞萍,便可以進行網(wǎng)絡(luò)通信了,iOS提供了兩種方式忘闻,一種是通過URL直接進行網(wǎng)絡(luò)請求钝计,一種是采用NSURLRequest對象進行請求;AFNetWorking將請求統(tǒng)一封裝成NSURLRequest對象服赎。
使用NSURLSession進行網(wǎng)絡(luò)請求葵蒂,需要選擇相應(yīng)的任務(wù)類型:數(shù)據(jù)任務(wù)類型(NSURLSessionDataTask)、上傳任務(wù)類型(NSURLSessionUploadTask)和下載任務(wù)類型(NSURLSessionDownloadTask)重虑。
對于數(shù)據(jù)任務(wù)類型践付,AFNetWorking(3.0)通過建立子類AFHTTPSessionManager對AFURLSessionManager類進行了進一步的抽象和封裝。這樣用戶在使用時缺厉,針對請求方法的不同選擇不同的網(wǎng)絡(luò)請求方法永高,只需傳入以下參數(shù)便可進行網(wǎng)絡(luò)請求:
- 請求地址
- 請求參數(shù)
- 處理請求成功的block
- 處理請求失敗的block
- 處理上傳進度block
- 處理下載進度block
雖然AFHTTPSessionMananger繼承于AFURLSessionManager,但由于有關(guān)NSURLSession的配置與操作全部封裝在AFURLSessionManager中提针,所以AFHTTPSessionMananger所要做的工作其實并不多命爬,主要是
- 根據(jù)傳入的URL和請求參數(shù)創(chuàng)建和修改NSURLResquest對象
- 根據(jù)用戶選擇的網(wǎng)絡(luò)請求方法,調(diào)用NSURLSessionManager對應(yīng)的方法辐脖,并對返回的NSURLSessionDataTask進行操作饲宛,所以AFHTTPSessionManager主要是用來進行數(shù)據(jù)任務(wù)的,有關(guān)上傳和下載的任務(wù)并不AFHTTPSessionManager中進行嗜价。
在AFHTTPSessionManager中僅包含三個屬性:
- NSString類型的URL
- 對請求進行序列化操作的AFHTTPRequestSerializer對象艇抠,封裝了對NSURLResquest對象進行操作的方法
- 對響應(yīng)進行序列化操作的AFHTTPResponseSerializer對象幕庐,封裝了對NSURLResponse對象進行操作的方法
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
AFHTTPSessionMananger提供了一些初始化的方法,這些方法都是基于如下方法:
- (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration
該方法對AFHTTPSessionMananger進行初始化操作家淤,并傳入?yún)?shù)configuration給AFURLSessionManager的初始化方法
AFHTTPSessionMananger提供了常用的網(wǎng)絡(luò)請求方法异剥,如:GET、POST絮重、HEAD冤寿、PUT、PATCH青伤、DELETE方法督怜,用戶所能調(diào)用方法都基于AFHTTPSessionMananger的以下兩個方法:
//進行網(wǎng)絡(luò)請求操作
- (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;
//進行請求體包含block的POST請求
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure;
對于上傳和下載任務(wù),開發(fā)者可以采用同樣的方式對請求參數(shù)進行封裝潮模,或者直接調(diào)用AFURLSessionManager中的請求方法亮蛔。
//上傳文件請求
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fileURL
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
//上傳數(shù)據(jù)類型
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
//上傳數(shù)據(jù)流
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler;
//下載請求
- (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;
//恢復(fù)下載請求
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler;
3. NSURLSessionTask的創(chuàng)建
要使用NSURLSession對象進行網(wǎng)絡(luò)通信,需要用該對象調(diào)用請求方法返回一個NSURLSessionTask對象擎厢,通過task對象開啟網(wǎng)絡(luò)請求究流。AFURLSessionManager會在task開啟網(wǎng)絡(luò)請求之前,將task與AFURLSessionManagerTaskDelegate對象通過字典mutableTaskDelegatesKeyedByTaskIdentifier進行映射动遭,以確保每一個task對象都會有一個taskDelegate對象芬探。
AFURLSessionManagerTaskDelegate類的定義在AFURLSessionManager中,它的作用主要是管理網(wǎng)絡(luò)請求任務(wù)的進度厘惦,因此也實現(xiàn)了NSURLSessionTaskDelegate偷仿、 NSURLSessionDataDelegate、NSURLSessionDownloadDelegate的部分方法宵蕉。task對象與taskDelegate對象進行映射的過程如下:
- 調(diào)用NSURLSession對象的方法返回一個task對象
- 創(chuàng)建一個taskDelegate對象酝静,將傳入的有關(guān)進度和出路請求完成的block賦給taskDelegate對象
- 以task對象的taskIdentifier標(biāo)識符為key,將taskDelegate對象添加到字典中
這里有一個問題需要解決羡玛,就是如何保證task對象的標(biāo)識符是唯一的别智?
在第一節(jié)中初始化AFURLSessionManager對象時,創(chuàng)建session對象時的委托是self稼稿,也就是AFURLSessionManager對象本身薄榛,AFNetWorking(3.0)在進行網(wǎng)絡(luò)請求時,所有的task對象都由該session對象產(chǎn)生让歼,而由同一個session產(chǎn)生的多個task敞恋,它們的標(biāo)識符都是唯一的;不同的session產(chǎn)生的task谋右,task的標(biāo)識符則不一定唯一硬猫。這樣做就能夠保證在進行多個網(wǎng)絡(luò)請求時,對每個網(wǎng)絡(luò)請求的task進行單獨管理,例如多個下載任務(wù)在執(zhí)行啸蜜,用戶可以決定哪個任務(wù)暫停馏予,哪個任務(wù)繼續(xù)下載。字典添加映射代碼如下盔性,以下載為例:
- (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
{
//1、創(chuàng)建一個AFURLSessionManagerTaskDelegate對象呢岗,將block參數(shù)賦給delegate
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
//2冕香、返回task對象
if (destination) {
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
}
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
//3、將task對象與delegate進行關(guān)聯(lián)
[self setDelegate:delegate forTask:downloadTask];
delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
//以task的標(biāo)識符為key后豫,將delegate放入字典
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
這里又出現(xiàn)了另一個問題悉尾,既然所有的task都是由同一個session產(chǎn)生,而該session對象的委托確是AFURLSessionManager對象本身挫酿,網(wǎng)絡(luò)請求的執(zhí)行會調(diào)用AFURLSessionManager實現(xiàn)的方法构眯,如何通過AFURLSessionManagerTaskDelegate對象來控制網(wǎng)絡(luò)請求任務(wù)的進行?
前面說過早龟,AFURLSessionManagerTaskDelegate實現(xiàn)了委托的部分方法惫霸,這部分方法控制著網(wǎng)絡(luò)請求的任務(wù)進度,而在AFURLSessionManager中也實現(xiàn)了這些方法葱弟,當(dāng)系統(tǒng)調(diào)用AFURLSessionManager中的實現(xiàn)方法時壹店,AFURLSessionManager根據(jù)task的標(biāo)識符在字典中找到對應(yīng)的AFURLSessionManagerTaskDelegate對象,若AFURLSessionManagerTaskDelegate對象中實現(xiàn)了該方法芝加,則調(diào)用AFURLSessionManagerTaskDelegate中實現(xiàn)的方法硅卢。
4. 網(wǎng)絡(luò)請求的進度管理
網(wǎng)絡(luò)請求的進度管理是由AFURLSessionManagerTaskDelegate負責(zé)的,AFURLSessionManagerTaskDelegate具有以下屬性:
@property (nonatomic, weak) AFURLSessionManager *manager; //指向AFURLSessionManager對象藏杖,能夠調(diào)用AFURLSessionManager中的block方法
@property (nonatomic, strong) NSMutableData *mutableData; //存放網(wǎng)絡(luò)請求接收到的數(shù)據(jù)
@property (nonatomic, strong) NSProgress *uploadProgress; //上傳任務(wù)進度
@property (nonatomic, strong) NSProgress *downloadProgress; //下載任務(wù)進度
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading; //下載完成回調(diào)
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock; //上傳任務(wù)進度回調(diào)
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock; //下載任務(wù)進度回調(diào)
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler; //處理回調(diào)
當(dāng)網(wǎng)絡(luò)進行網(wǎng)絡(luò)通信時将塑,AFURLSessionManagerTaskDelegate中實現(xiàn)的委托方法會返回進度信息并記錄到uploadProgress和downloadProgress中,同時接收到的數(shù)據(jù)也會存放到mutableData中蝌麸。為了能夠響應(yīng)進度信息的變化点寥,AFURLSessionManagerTaskDelegate則通過KVO的對進度信息監(jiān)聽,一旦進度信息發(fā)生改變祥楣,則可以調(diào)用回調(diào)進行處理开财,例如可以在回調(diào)中刷新主界面。這些回調(diào)函數(shù)都是在作為網(wǎng)絡(luò)請求的參數(shù)傳入到AFURLSessionManager中误褪,并由AFURLSessionManager創(chuàng)建任務(wù)時賦給AFURLSessionManagerTaskDelegate對象的责鳍。
//設(shè)置uploadProgress與downloadProgress,并添加觀察者
- (void)setupProgressForTask:(NSURLSessionTask *)task {
//#1兽间、對downloadProgress和uploadProgress進行設(shè)置历葛,代碼略
//#2、添加對downloadProgress和uploadProgress的KVO觀察者
[self.downloadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.uploadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
//移除觀察者
- (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
[self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
[self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
//添加默認的監(jiān)聽方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
//調(diào)用downloadProgress的回調(diào)
if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
//調(diào)用uploadProgress的回調(diào)
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
當(dāng)NSURLSessionTask結(jié)束時,總會調(diào)用委托方法URLSession:task:didCompleteWithError: ,該方法告訴委托當(dāng)前的網(wǎng)絡(luò)數(shù)據(jù)通信已完成恤溶。在AFNetWorking(3.0)中乓诽,該方法的主要工作如下:
- 從manager中獲取本次請求的response序列化信息,放入userInfo字典
- 將網(wǎng)絡(luò)通信接收的數(shù)據(jù)放入userInfo字典
- 若網(wǎng)絡(luò)通信錯誤咒程,則將error放入userInfo字典鸠天,同時使用處理回調(diào)進行后續(xù)處理,并發(fā)送通知
- 若網(wǎng)絡(luò)通信正常帐姻,則將response序列化信息進行解析稠集,放入userInfo字典中,同時調(diào)用處理回調(diào)進行后續(xù)處理饥瓷,并發(fā)送通知
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
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 (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (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(), ^{
[[NSNotificationCenter defaultCenter] 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(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
}
以上就是我閱讀AFNetWorking(3.0)的源碼后做的關(guān)于NSURLSession的使用的總結(jié)剥纷。AFNetWorking(3.0)作為一個成熟、穩(wěn)定的網(wǎng)絡(luò)請求框架呢铆,所涉及的知識也不局限于NSURLSession的使用晦鞋,還有許多內(nèi)容,如網(wǎng)絡(luò)狀態(tài)的監(jiān)測棺克、網(wǎng)絡(luò)請求的安全性處理悠垛,這些都非常值得我們花時間去學(xué)習(xí)。