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ī)避了很多問題。