在AFNetworking到底長(zhǎng)啥樣(上)中簡(jiǎn)單介紹了AFN涉及的主要類及其結(jié)構(gòu),接下來(lái)以一個(gè)簡(jiǎn)單的POST請(qǐng)求探尋其內(nèi)部是如何實(shí)現(xiàn)的。
10余年的吳興網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)整合營(yíng)銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整吳興建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。成都創(chuàng)新互聯(lián)公司從事“吳興網(wǎng)站設(shè)計(jì)”,“吳興網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
服務(wù)器配置
本例中直接使用iMac自帶的Apache,并為其開(kāi)啟PHP支持。在服務(wù)器目錄下編寫index.php文件如下:
<?php
echo @"This is Layne's Response";
?>
編寫測(cè)試App
創(chuàng)建一個(gè)測(cè)試App,在主界面上增加一個(gè)按鈕,在按鈕的點(diǎn)擊函數(shù)中發(fā)起網(wǎng)絡(luò)請(qǐng)求,如下:
- (AFHTTPSessionManager *)manager{//lazy
if(!_manager){
_manager = [AFHTTPSessionManager manager];
}
return _manager;
}
- (void)click{
[self.manager POST:@"http://www.layne.com"
parameters:@{@"name":@"layne",@"age":@30}
headers:@{@"TestName":@"myTest"}
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"success:%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"fail:%@",error);
}];
}
啟動(dòng)測(cè)試App,點(diǎn)擊按鈕。接下里讓我們看看AFN是如何優(yōu)雅的管理網(wǎng)絡(luò)請(qǐng)求的。
AFHTTPSessionManager的初始化
self.manager
是AFHTTPSessionManager
的實(shí)例,它使用類方法+ manager
初始化。這個(gè)類方法最終調(diào)用如下方法:
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];//①configuration=nil
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];//②
self.responseSerializer = [AFJSONResponseSerializer serializer];//③
return self;
}
①中調(diào)用父類的方法初始化父類相關(guān)屬性,這包括:
operationQueue
responseSerializer(為AFJSONResponseSerializer實(shí)例)
securityPolicy(為默認(rèn),即不進(jìn)行SSL驗(yàn)證)
reachabilityManager
mutableTaskDelegatesKeyedByTaskIdentifier(管理{taskID:delegate})
lock(用于操作mutableTaskDelegatesKeyedByTaskIdentifier時(shí)保證線程安全)
session(NSURLSession實(shí)例,設(shè)置其代理為self,queue為operationQueue)
②中設(shè)置其requestSerializer為AFHTTPRequestSerializer實(shí)例,③重設(shè)其responseSerializer為AFJSONResponseSerializer實(shí)例。
AFHTTPRequestSerializer的初始化
requestSerializer被設(shè)置成了AFHTTPRequestSerializer的實(shí)例,初始化是通過(guò)類方法+serializer
實(shí)現(xiàn)的。內(nèi)容包括:
stringEncoding(為NSUTF8StringEncoding)
mutableHTTPRequestHeaders(保存用戶設(shè)置的header,默認(rèn)會(huì)添加"Accept-Language"和"User-Agent",其中"User-Agent"還進(jìn)行了ICU轉(zhuǎn)換以保證必須為ASCII字符)
HTTPMethodsEncodingParametersInURI(為GET、HEAD、DELETE)
mutableObservedChangedKeyPaths (監(jiān)聽(tīng)"allowsCellularAccess"、"cachePolicy"、"HTTPShouldHandleCookies"、"HTTPShouldUsePipelining"、"networkServiceType"、"timeoutInterval",一旦用戶設(shè)置了這6個(gè)屬性,則對(duì)應(yīng)屬性的名會(huì)被存入,最后其值會(huì)加到request中去,本質(zhì)是增量添加:“誰(shuí)改變添加誰(shuí)”)
AFJSONResponseSerializer的初始化
responseSerializer被設(shè)置成為了AFJSONResponseSerializer的實(shí)例,初始化是通過(guò)類方法+serializer
實(shí)現(xiàn)的。內(nèi)容包括:
readingOptions(為0)
acceptableStatusCodes(可接受的code,初始化為[200~199])
acceptableContentTypes(可接受的contenttype,設(shè)置為application/json、text/json、text/javascript)
注:其父類AFHTTPResponseSerializer的acceptableContentTypes=nil,即接受任何Content。
至此必要的初始化已完成。
進(jìn)入到POST函數(shù)中:
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters headers:headers uploadProgress:uploadProgress downloadProgress:nil success:success failure:failure];
[dataTask resume];
return dataTask;
}
即最終生成一個(gè)dataTask,然后resume啟動(dòng),并將dataTask返回。
生成的dataTask是通過(guò)如下函數(shù)實(shí)現(xiàn)的,本例中傳入的參數(shù)值也已標(biāo)明:
- dataTaskWithHTTPMethod: //@"POST"
URLString: //@"http://www.layne.com"
parameters: //@{@"name":@"layne",@"age":@30}
headers: //@{@"TestName":@"myTest"}
uploadProgress: //nil
downloadProgress: //nil
success: //successBlock{NSLog(@"success:%@",responseObject);}
failure: //failureBlock{NSLog(@"fail:%@",error);}
其內(nèi)部執(zhí)行的步驟如下:
Step1:調(diào)用requestSerializer的如下方法創(chuàng)建mutableRequest:
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method //@"POST"
URLString:(NSString *)URLString//@"http://www.layne.com"
parameters:(id)parameters//@{@"name":@"layne",@"age":@30}
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;//設(shè)置為POST
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}//根據(jù)用戶是否設(shè)置了allowsCellularAccess、cachePolicy、HTTPShouldHandleCookies、HTTPShouldUsePipelining、networkServiceType、timeoutInterval來(lái)設(shè)置mutableRequest對(duì)應(yīng)字段值。本例中由于未進(jìn)行任何設(shè)置,因此上述邏輯不會(huì)有效果。
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];//①
return mutableRequest;
}
①是調(diào)用AFURLRequestSerialization協(xié)議方法對(duì)mutableRequest進(jìn)行進(jìn)一步處理,包括:
設(shè)置默認(rèn)header,如在requestSerializer初始化時(shí)設(shè)置默認(rèn)header:User-Agent和Accept-Language
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
處理參數(shù)
若用戶自定義了處理參數(shù)的block(self.queryStringSerialization),則使用該block;否則使用默認(rèn)的處理方式,會(huì)被最終處理成如下格式:name=layne&age=30。
至此,生成的mutableRequest結(jié)構(gòu)如下:
mutableRequest{
URL: "http://www.layne.com"
Method: "POST"
Header: "Accept-Language" = "en;q=1";
"User-Agent" = "TestApp/1.0 (iPhone; iOS 13.1; Scale/3.00)";
"Content-Type" = "application/x-www-form-urlencoded";
HttpBody <6167653d 3330266e 616d653d 6c61796e 65> //name=layne&age=30(utf-8)
}
Step2:合并傳入的header
for (NSString *headerField in headers.keyEnumerator) {
[request addValue:headers[headerField] forHTTPHeaderField:headerField];
}
執(zhí)行之后mutableRequest結(jié)構(gòu)如下:
mutableRequest{
URL: "http://www.layne.com"
Method: "POST"
Header: "Accept-Language" = "en;q=1";
"User-Agent" = "TestApp/1.0 (iPhone; iOS 13.1; Scale/3.00)";
"Content-Type" = "application/x-www-form-urlencoded";
"TestName":"myTest";
HttpBody <6167653d 3330266e 616d653d 6c61796e 65> //name=layne&age=30(utf-8)
}
Step3:判斷Step1中生成mutableRequest的過(guò)程是否出錯(cuò),若出錯(cuò),則調(diào)用failureBlock并返回。
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
Step4:根據(jù)mutableRequest生成dataTask。
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request //request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock//nil
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock//nil
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler /*block{
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}*/
{
__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;//③
}
說(shuō)明:
①中不直接生成dataTask,是因?yàn)樵趇OS8以下的系統(tǒng)上,若是在并行隊(duì)列上創(chuàng)建dataTask會(huì)導(dǎo)致completionHandler調(diào)用出錯(cuò)。因此為了解決這個(gè)問(wèn)題,針對(duì)iOS8以下系統(tǒng),AFN使用自己維護(hù)的一個(gè)串行隊(duì)列來(lái)創(chuàng)建dataTask。具體問(wèn)題描述如下:
Due to this bug: http://openradar.appspot.com/radar?id=5871104061079552 in NSURLSessionTask, creating tasks on a concurrent queue can cause incorrect completionHandlers to get called.
When a duplicate taskIdentifier is returned by the task, the previous completionHandler gets cleared out and replaced with the new one. If the data for the first request comes back before the second request's data, the first response is then called against the second completionHandler.
I'm not sure what AFNetworking should do here — it could enforce creating tasks on a serial queue or it could just advise people to do so. We could also add a test to assert that the taskIdentifier is not a duplicate?
②的作用是為dataTask生成對(duì)應(yīng)的delegate,以調(diào)用回調(diào)方法。
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask //dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock//nil
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock//nil
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler/*block{
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}*/
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];//a
delegate.manager = self;//b
delegate.completionHandler = completionHandler;//c
dataTask.taskDescription = self.taskDescriptionForSessionTasks;//d
[self setDelegate:delegate forTask:dataTask];//e
delegate.uploadProgressBlock = uploadProgressBlock;//nil
delegate.downloadProgressBlock = downloadProgressBlock;//nil
}
a. 為dataTask生成AFURLSessionManagerTaskDelegate實(shí)例。delegate內(nèi)部維護(hù)著:
一個(gè)mutableData用來(lái)保存收到的response data。
一個(gè)uploadProgress和downloadProgress用來(lái)標(biāo)明上傳/下載進(jìn)度。它們的cancel、suspend和resume操作與dataTask進(jìn)行了綁定,即通過(guò)對(duì)它們進(jìn)行cancel、suspend和resume操作就可以操作dataTask的cancel、suspend和resume。此外,還采用KVO監(jiān)聽(tīng)uploadProgress和downloadProgress的進(jìn)度變化,從而調(diào)用用戶自定義的block:uploadProgressBlock和downloadProgressBlock。
b. delegate弱引用當(dāng)前的manager。
c.delegate保存完成回調(diào)。
③返回最終的dataTask,并啟動(dòng)。
AFN是基于NSURLSession的,涉及以下幾個(gè)協(xié)議:
NSURLSessionDelegate
- URLSession:didBecomeInvalidWithError:
- URLSession:didReceiveChallenge:completionHandler:
- URLSessionDidFinishEventsForBackgroundURLSession:
NSURLSessionTaskDelegate
- URLSession:willPerformHTTPRedirection:newRequest:completionHandler:
- URLSession:task:didReceiveChallenge:completionHandler:
- URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
- URLSession:task:needNewBodyStream:
- URLSession:task:didCompleteWithError:
- URLSession:task:didFinishCollectingMetrics:
NSURLSessionDataDelegate
- URLSession:dataTask:didReceiveResponse:completionHandler:
- URLSession:dataTask:didBecomeDownloadTask:
- URLSession:dataTask:didReceiveData:
- URLSession:dataTask:willCacheResponse:completionHandler:
NSURLSessionDownloadDelegate
- URLSession:downloadTask:didFinishDownloadingToURL:
- URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
- URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
回調(diào)調(diào)用順序:
① URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
由于使用的是POST方式,因此該回調(diào)是首先調(diào)用的。邏輯:找到task對(duì)應(yīng)的delegate=>調(diào)用delegate的URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:來(lái)更新delegate維護(hù)的uploadProgress。若用戶自定義了taskDidSendBodyData block,則調(diào)用。
② URLSession:task:didFinishCollectingMetrics:
該方法調(diào)用時(shí)機(jī)不定,用來(lái)收集整個(gè)請(qǐng)求的信息。邏輯:找到task對(duì)應(yīng)的delegate=>調(diào)用delegate的URLSession:task:didFinishCollectingMetrics:來(lái)為delegate.sessionTaskMetrics賦值。若用戶自定義了taskDidFinishCollectingMetrics block,則調(diào)用。
③ URLSession:dataTask:didReceiveData:
收到response data時(shí)執(zhí)行。邏輯:找到task對(duì)應(yīng)的delegate=>調(diào)用delegate的URLSession:task:didCompleteWithError:來(lái)更新delegate內(nèi)部維護(hù)的downloadProgress并使用mutableData保存response data。若用戶自定義了dataTaskDidReceiveData block,則調(diào)用。
④ URLSession:task:didCompleteWithError:
這是最后執(zhí)行的回調(diào)。邏輯:找到task對(duì)應(yīng)的delegate=>調(diào)用delegate的URLSession:task:didCompleteWithError:,并刪除delegate和task的對(duì)應(yīng)關(guān)系。若用戶自定義了taskDidComplete block,則調(diào)用。
數(shù)據(jù)的處理主要在delegate的URLSession:task:didCompleteWithError:中,主要做了以下幾件工作:
構(gòu)造userInfo字典:
userInfo{
AFNetworkingTaskDidCompleteResponseSerializerKey:<AFJSONResponseSerializer: 0x600003db6820>,
AFNetworkingTaskDidCompleteSessionTaskMetrics:"...",
AFNetworkingTaskDidCompleteResponseDataKey:"<length=24,bytes=0x567283d>"http://response data
/*若出錯(cuò)則會(huì)包含以下key*/
AFNetworkingTaskDidCompleteErrorKey:"error" //error數(shù)據(jù)
}
若出錯(cuò),則直接調(diào)用completeHandler回調(diào),之后將上面的userInfo以通知AFNetworkingTaskDidCompleteNotification的形式廣播出去。
經(jīng)過(guò)上面的處理,最終要么error要么responseObject會(huì)返回到POST的回調(diào)中去。整個(gè)網(wǎng)絡(luò)請(qǐng)求就完成了。
上面我們跟著一個(gè)簡(jiǎn)單的POST請(qǐng)求了解了AFN的整個(gè)工作流程,但還有一些細(xì)節(jié)我覺(jué)得還是值得我們學(xué)習(xí)的。
如果response不是標(biāo)準(zhǔn)格式的JSON數(shù)據(jù)或者我們需要對(duì)原始data進(jìn)行加密解密操作該如何做?
答:將responseSerializer設(shè)置為AFHTTPResponseSerializer的實(shí)例,這樣response data 會(huì)以原始data的形式返回給上層。AFHTTPResponseSerializer僅針對(duì)code和contenttype進(jìn)行校驗(yàn),而默認(rèn)的AFJSONResponseSerializer除了校驗(yàn)code和contenttype之外還對(duì)JSON格式進(jìn)行校驗(yàn),并直接解析成NSDictionary。當(dāng)然,如果想對(duì)response data想要做最全面的自定義處理,最直接的方式當(dāng)然是自定義AFHTTPResponseSerializer的子類,并重寫協(xié)議方法
- (id)responseObjectForResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error
如果返回的JSON數(shù)據(jù)中包含NSNull數(shù)據(jù)該如何處理?
答:將responseSerializer(AFJSONResponseSerializer實(shí)例)的removesKeysWithNullValues屬性設(shè)置為YES。這樣一來(lái)在JSON轉(zhuǎn)換為NSDictionary之后會(huì)將NSDictionary中的NSNull值去除。
如果我想自己更改請(qǐng)求參數(shù)的格式(即不用默認(rèn)的name=layne&age=30這種)該如何設(shè)置?
答:調(diào)用requestSerializer的-setQueryStringSerializationWithBlock:設(shè)置自定義的格式。在requestSerializer處理參數(shù)的時(shí)候會(huì)先去判斷queryStringSerialization block是否為空,若不為空,則使用該block處理參數(shù)。若為空,則使用默認(rèn)的格式(如name=layne&age=30)生成參數(shù)串。
除了使用GET/POST方法的回調(diào),還可以通過(guò)什么方式獲得網(wǎng)絡(luò)請(qǐng)求結(jié)果?
答:還可以使用notification。AFN中有一個(gè)名為AFNetworkingTaskDidCompleteNotification的通知,可以通過(guò)監(jiān)聽(tīng)該通知獲取網(wǎng)絡(luò)請(qǐng)求結(jié)果。AFURLSessionManagerTaskDelegate的URLSession:task:didCompleteWithError:中在調(diào)用completeHandler之后會(huì)發(fā)送通知:
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);//回調(diào)
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];//將task和userInfo一起廣播出去
});
});
如何更改回調(diào)所在的線程?
答:指定manager的completeQueue。這樣回調(diào)就會(huì)在其他線程中執(zhí)行。
_manager.completionQueue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
AFURLSessionManager如何獲取NSURLSession維護(hù)的task?
答:使用getTasksWithCompletionHandler:API并使用信號(hào)量保證線程安全。
- (NSArray *)tasksForKeyPath:(NSString *)keyPath { //tasks、uploadTask、downloadTasks
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
若我想取消一個(gè)剛發(fā)起的網(wǎng)絡(luò)請(qǐng)求該如何做?
答:AFHTTPSessionManager的POST/GET方法返回的是task對(duì)象,直接[task cancel]即可。如:
NSURLSessionDataTask *task = [self.manager POST:@"http://www.layne.com"
parameters:@{@"name":@"layne",@"age":@30}
headers:@{@"TestName":@"myTest"}
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"success:%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"fail:%@",error);
}];
[task cancel];//直接cancel
若我想取消所有task該如何做?
答:使用如下方法。建議resetSession傳入YES將session重置,否則下次網(wǎng)絡(luò)請(qǐng)求會(huì)crash,并提示:“Attempted to create a task in a session that has been invalidated”
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks resetSession:(BOOL)resetSession {
if (cancelPendingTasks) {
[self.session invalidateAndCancel];
} else {
[self.session finishTasksAndInvalidate];
}
if (resetSession) {
self.session = nil;
}
}
例如:
[self.manager invalidateSessionCancelingTasks:YES resetSession:YES];
至此AFNetworking是如何工作的我們就知道了。看了源碼之后不得不感嘆作者真是神人,不僅優(yōu)雅的給出了NSURLSession的使用范例,而且還包含了業(yè)務(wù)層面的巧妙設(shè)計(jì)。嗯,閱讀源碼使我快樂(lè)^_^。
文章題目:AFNetworking到底長(zhǎng)啥樣(下)
標(biāo)題鏈接:http://chinadenli.net/article16/ppgsgg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站制作、建站公司、靜態(tài)網(wǎng)站、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)