AFNetworking詳解

AFNetWorking幾乎是iOS開發(fā)中最為廣泛被使用的一個(gè)三方開源組件,主要幫助開發(fā)者實(shí)現(xiàn)和管理iOS項(xiàng)目中的http/https網(wǎng)絡(luò)請(qǐng)求。目前已經(jīng)發(fā)展到4.0版本

下面簡(jiǎn)單描述下各個(gè)版本的差異:

AFNetworking 1.0 是基于NSURLConnection開發(fā)出來的。而NSURLConnection是蘋果早些年提供的網(wǎng)絡(luò)通訊的API接口。目前該接口已經(jīng)廢棄。

AFNetworking 2.0 是基于部分NSURLConnection接口 和部分NSURLSession接口開發(fā)的。簡(jiǎn)單來說,2.0是介于NSURLConnection和NSURLSession的過渡階段。其中NSURLSession接口是蘋果提供且目前主推的網(wǎng)絡(luò)通訊API接口。

AFNetworking 3.0 完全基于NSURLSession開發(fā),此版本中的NSURLConnection全部棄用。這樣不僅降低了代碼維護(hù)工作,還更好地支持了NSURLSession提供的額外功能。

AFNetworking 4.0 是2020年發(fā)布的。主要是配合蘋果公司棄用UIWebView控件的升級(jí),同時(shí)也移除之前棄用的API接口。不過要特別說明,這個(gè)版本支持的iOS版是9.0(之前是7.0),macOS 10.10。

通過以上AFNetWorking的版本演變歷史,我們也可以看到iOS網(wǎng)絡(luò)接口相關(guān)類庫的演變,從最初的NSURLConnection、到后來的NSURLSession,以及后來拋棄UIWebView,使用WKWebView等等變化。這里需要說明的一點(diǎn)AFNetworking只面向Object-C語言,如果需要使用swift語言的話,可以使用Alamofire庫或者原生。


1、拋開AFNetworking直接使用原生是否可以實(shí)現(xiàn)網(wǎng)絡(luò)接口

2、如果原生可以實(shí)現(xiàn),為什么要引入AFNetworking

3、AFNetworking在原生的基礎(chǔ)上做了哪些事情


使用原生發(fā)送一個(gè)接口

NSURLSession* session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

? ? NSString*urlString = [[NSStringalloc]initWithFormat:@"https://www.baidu.com"];

? ? NSURL*url = [[NSURLalloc]initWithString:urlString];

? ? NSURLRequest*baiduRequest = [[NSURLRequestalloc]initWithURL:url];

? ? NSURLSessionTask*task = [sessiondataTaskWithRequest:baiduRequestcompletionHandler:^(NSData*_Nullabledata,NSURLResponse*_Nullableresponse,NSError*_Nullableerror) {

? ? ? ? NSLog([[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

? ? }];

? ? [taskresume];


之前初始化session的時(shí)候大家看到了,為session設(shè)置了一個(gè)delegate和delegate消息響應(yīng)的線程。這里要說明一下,如果初始化一個(gè)task時(shí),使用了下面的這種簡(jiǎn)易方法,傳入了一個(gè)handler來處理返回消息,那么該task就不會(huì)再調(diào)用session對(duì)象的delegate方法,使用這里傳入的block代替了delegate方法。但是session對(duì)象設(shè)置的代理響應(yīng)的線程仍然起作用,比如上面我初始化session的時(shí)候,傳入的delegateQueue參數(shù)是主線程,那么這里handler這個(gè)block返回時(shí)也是在主線程中。

/*初始化一個(gè)session對(duì)象,其中三個(gè)參數(shù)分別是

? ? *configuration:配置對(duì)象

? ? *delegate:session處理代理的對(duì)象

? ? *delegateQueue:代理的消息處理的線程,這里傳mainQueue,代理的消息都會(huì)在主線程中收到

? ? */

? ? self.curSession = [NSURLSession sessionWithConfiguration:yConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];


使用AFNetworking發(fā)送一個(gè)接口

static NSString * const AFAppDotNetAPIBaseURLString = @"https://api.app.net/";

? ? AFHTTPSessionManager *sharedClient = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:AFAppDotNetAPIBaseURLString]];

? ? ? ? sharedClient.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];

[sharedClient GET:@"stream/0/posts/stream/global"parameters:nilheaders:nilprogress:nilsuccess:^(NSURLSessionDataTask *__unusedtask,idJSON) {

? ? ? ? NSArray *postsFromResponse = [JSON valueForKeyPath:@"data"];

? ? ? ? NSMutableArray *mutablePosts = [NSMutableArray arrayWithCapacity:[postsFromResponse count]];

? ? ? ? for(NSDictionary *attributesinpostsFromResponse) {

? ? ? ? ? ? Post *post = [[Post alloc] initWithAttributes:attributes];

? ? ? ? ? ? [mutablePosts addObject:post];

? ? ? ? }

? ? ? ? if(block) {

? ? ? ? ? ? block([NSArray arrayWithArray:mutablePosts],nil);

? ? ? ? }

? ? } failure:^(NSURLSessionDataTask *__unusedtask, NSError *error) {

? ? ? ? if(block) {

? ? ? ? ? ? block([NSArray array], error);

? ? ? ? }

? ? }];



1、構(gòu)建request

NSMutableURLRequest*request = [self.requestSerializerrequestWithMethod:methodURLString:[[NSURLURLWithString:URLStringrelativeToURL:self.baseURL]absoluteString]parameters:parameterserror:&serializationError];

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? URLString:(NSString*)URLString

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? parameters:(id)parameters

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? error:(NSError*__autoreleasing*)error

{

? ? NSParameterAssert(method);

? ? NSParameterAssert(URLString);

? ? NSURL*url = [NSURLURLWithString:URLString];

? ? NSParameterAssert(url);

? ? NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];

? ? mutableRequest.HTTPMethod= method;

? ? for (NSString *keyPath in self.mutableObservedChangedKeyPaths) {

? ? ? ? [mutableRequestsetValue:[selfvalueForKeyPath:keyPath]forKey:keyPath];

? ? }

? ? mutableRequest = [[selfrequestBySerializingRequest:mutableRequestwithParameters:parameterserror:error]mutableCopy];

returnmutableRequest;

}


@property (readwrite, nonatomic, strong) NSMutableSet *mutableObservedChangedKeyPaths;


self.mutableObservedChangedKeyPaths = [NSMutableSet set];

? ? for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {

? ? ? ? if ([self respondsToSelector:NSSelectorFromString(keyPath)]) {

? ? ? ? ? ? [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:AFHTTPRequestSerializerObserverContext];

? ? ? ? }

? ? }


static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {

? ? staticNSArray*_AFHTTPRequestSerializerObservedKeyPaths =nil;

? ? staticdispatch_once_tonceToken;

? ? dispatch_once(&onceToken, ^{

? ? ? ? _AFHTTPRequestSerializerObservedKeyPaths =@[NSStringFromSelector(@selector(allowsCellularAccess)),NSStringFromSelector(@selector(cachePolicy)),NSStringFromSelector(@selector(HTTPShouldHandleCookies)),NSStringFromSelector(@selector(HTTPShouldUsePipelining)),NSStringFromSelector(@selector(networkServiceType)),NSStringFromSelector(@selector(timeoutInterval))];

? ? });

? ? return_AFHTTPRequestSerializerObservedKeyPaths;

}

參數(shù)構(gòu)建,request參數(shù)設(shè)置,KVO,KVC講解,用的巧妙,反省我自己寫的話大概率就是做一個(gè)類,類中有很多屬性,屬性初始化的時(shí)候是null,設(shè)置了值之后,在每次創(chuàng)建request的時(shí)候,久判斷哪個(gè)不是null就設(shè)置哪個(gè)。

同時(shí)大概講解一下request中那些參數(shù)的意義。

allowsCellularAccess:(BOOL)allowsCellularAccess

返回值: YES蜂窩數(shù)據(jù)可用,NO蜂窩數(shù)據(jù)不可用。

cachePolicy:請(qǐng)求使用的緩存策略

(BOOL)HTTPShouldHandleCookies

返回值: YES 使用默認(rèn)cookie處理,NO不使用。

默認(rèn)值為YES。

(NSURLRequestNetworkServiceType)networkServiceType

返回值: 網(wǎng)絡(luò)服務(wù)類型。

網(wǎng)絡(luò)服務(wù)類型給操作系統(tǒng)提示底層通信的作用。這個(gè)提示有助于系統(tǒng)優(yōu)化通信,確定喚醒蜂窩數(shù)據(jù)或者WIFI的速度。調(diào)節(jié)不同的參數(shù),可以平衡電池、性能以及其他因素。

比如,進(jìn)行非用戶請(qǐng)求的下載時(shí)應(yīng)該使用 NSURLNetworkServiceTypeBackground。 比如,在后臺(tái)提前加載數(shù)據(jù),這樣等用戶需要看時(shí)就不需要加載了。

(NSTimeInterval)timeoutInterval

返回值: 請(qǐng)求的超時(shí)時(shí)間,單位秒。


2、多線程相關(guān)處理

[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response,NSData *data,NSError *connectionError) {// 有的時(shí)候,服務(wù)器訪問正常,但是會(huì)沒有數(shù)據(jù)!// 以下的 if 是比較標(biāo)準(zhǔn)的錯(cuò)誤 處理代碼!if (connectionError !=nil || data ==nil) {//給用戶的提示信息NSLog(@"網(wǎng)絡(luò)不給力");return;

? ? }

}];

底層發(fā)送使用socket接口,這塊我們認(rèn)為NSURLConnection與NSURLSession一致

maxConcurrentOperationCount =1的設(shè)置

多線程回調(diào)的歷史問題

3、眾多代理的處理和回調(diào)

我們把AFUrlSessionManager作為了所有的task的delegate。當(dāng)我們請(qǐng)求網(wǎng)絡(luò)的時(shí)候,這些代理開始調(diào)用了:


AFUrlSessionManager一共實(shí)現(xiàn)了如上圖所示這么一大堆NSUrlSession相關(guān)的代理。(小伙伴們的順序可能不一樣,樓主根據(jù)代理隸屬重新排序了一下)

而只轉(zhuǎn)發(fā)了其中3條到AF自定義的delegate中:


這就是我們一開始說的,AFUrlSessionManager對(duì)這一大堆代理做了一些公共的處理,而轉(zhuǎn)發(fā)到AF自定義代理的3條,則負(fù)責(zé)把每個(gè)task對(duì)應(yīng)的數(shù)據(jù)回調(diào)出去。

鏈接:http://www.itdecent.cn/p/856f0e26279d

4、證書校驗(yàn)

- (void)URLSession:(NSURLSession*)session

? ? ? ? ? ? ? task:(NSURLSessionTask*)task

didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge

completionHandler:(void(^)(NSURLSessionAuthChallengeDispositiondisposition,NSURLCredential*credential))completionHandler

{

? ? BOOLevaluateServerTrust =NO;

? ? NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;

? ? NSURLCredential*credential =nil;

? ? if (self.authenticationChallengeHandler) {

? ? ? ? idresult =self.authenticationChallengeHandler(session, task, challenge, completionHandler);

? ? ? ? if(result ==nil) {

? ? ? ? ? ? return;

? ? ? ? }elseif([resultisKindOfClass:NSError.class]) {

? ? ? ? ? ? objc_setAssociatedObject(task, AuthenticationChallengeErrorKey, result, OBJC_ASSOCIATION_RETAIN);

? ? ? ? ? ? disposition =NSURLSessionAuthChallengeCancelAuthenticationChallenge;

? ? ? ? }else if ([result isKindOfClass:NSURLCredential.class]) {

? ? ? ? ? ? credential = result;

? ? ? ? ? ? disposition =NSURLSessionAuthChallengeUseCredential;

? ? ? ? }elseif([resultisKindOfClass:NSNumber.class]) {

? ? ? ? ? ? disposition = [resultintegerValue];

? ? ? ? ? ? NSAssert(disposition == NSURLSessionAuthChallengePerformDefaultHandling || disposition == NSURLSessionAuthChallengeCancelAuthenticationChallenge || disposition == NSURLSessionAuthChallengeRejectProtectionSpace, @"");

? ? ? ? ? ? evaluateServerTrust = disposition ==NSURLSessionAuthChallengePerformDefaultHandling && [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];

? ? ? ? }else{

? ? ? ? ? ? @throw [NSException exceptionWithName:@"Invalid Return Value" reason:@"The return value from the authentication challenge handler must be nil, an NSError, an NSURLCredential or an NSNumber." userInfo:nil];

? ? ? ? }

? ? }else{

? ? ? ? evaluateServerTrust = [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];

? ? }

? ? if(evaluateServerTrust) {

? ? ? ? if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {

? ? ? ? ? ? disposition =NSURLSessionAuthChallengeUseCredential;

? ? ? ? ? ? credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];

? ? ? ? }else{

? ? ? ? ? ? objc_setAssociatedObject(task, AuthenticationChallengeErrorKey,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [selfserverTrustErrorForServerTrust:challenge.protectionSpace.serverTrusturl:task.currentRequest.URL],

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC_ASSOCIATION_RETAIN);

? ? ? ? ? ? disposition =NSURLSessionAuthChallengeCancelAuthenticationChallenge;

? ? ? ? }

? ? }

? ? if(completionHandler) {

? ? ? ? completionHandler(disposition, credential);

? ? }

}

NSURLSessionConfiguration 安全相關(guān)的策略

5、返回?cái)?shù)據(jù)處理

整理回顧,總結(jié)AF到底在原生基礎(chǔ)上做了哪幾件事情

1、2、3、4、5、

總結(jié)什么情況下用AF,什么情況下用原生

比如之前說的回調(diào)線程最大并發(fā)設(shè)置為1,如果我們有這種不為1的要求,我們就可以自己使用原生開發(fā),但是大部分情況下讓回調(diào)并發(fā)為1,給我們規(guī)避了很多問題。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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