AFNetworking粗解(AFURLConnectionOperation模塊)

111179.png

上一篇我們了解了AFNetworking各個(gè)模塊的功能,今天我們來了解一下AFNetworking源碼的實(shí)現(xiàn)
我們先看看AFURLConnectionOperation 作為AFNetworking最核心的類到底有什么神奇之處溪椎!
1.線程
先來看看 NSURLConnection 發(fā)送請求時(shí)的線程情況普舆,NSURLConnection 是被設(shè)計(jì)成異步發(fā)送的,調(diào)用了start方法后校读,NSURLConnection 會新建一些線程用底層的 CFSocket 去發(fā)送和接收請求沼侣,在發(fā)送和接收的一些事件發(fā)生后通知原來線程的Runloop去回調(diào)事件。
NSURLConnection 的同步方法 sendSynchronousRequest 方法也是基于異步的歉秫,同樣要在其他線程去處理請求的發(fā)送和接收蛾洛,只是同步方法會手動block住線程,發(fā)送狀態(tài)的通知也不是通過 RunLoop 進(jìn)行雁芙。
NSURLConnection發(fā)送有以下三種方式
? 在主線程調(diào)異步接口
? 在子線程調(diào)同步接口
? 在子線程調(diào)異步接口
AFNetworking使用的是最后一種方式方法轧膘,AFNetworking內(nèi)部相關(guān)線程大致的關(guān)系如下圖所示
NSURLConnection是一個(gè)系統(tǒng)控件,所以我們可以把NSURLConnection當(dāng)做一個(gè)黑盒却特,只管它的 start 和 callback 就行了扶供。如果使用 AFHttpRequestOperationManager 的接口發(fā)送請求,這些請求會統(tǒng)一在一個(gè) NSOperationQueue 里去發(fā)裂明,所以多了上面 NSOperationQueue 的一個(gè)線程椿浓。

相關(guān)代碼:
[objc] view plaincopy

1.  //-------------------------線程--------------------  
2.  /* 
3.      子線程調(diào)用異步接口太援,子線程需要有 Runloop 去接收異步回調(diào)事件,這里也可以每個(gè)請求都新建一條 
4.      帶有 Runloop 的線程去偵聽回調(diào)扳碍,但這一點(diǎn)好處都沒有提岔,既然是異步回調(diào),除了處理回調(diào)內(nèi)容笋敞,其他 
5.      時(shí)間線程都是空閑可利用的碱蒙,所有請求共用一個(gè)響應(yīng)的線程就夠了。 
6.   */  
7.  //獲取當(dāng)前的NSRunLoop夯巷,子線程使用共同的Runloop  
8.  + (void)networkRequestThreadEntryPoint:(id)__unused object {  
9.      @autoreleasepool {  
10.         [[NSThread currentThread] setName:@"AFNetworking"];  
11.         NSRunLoop *runLoop = [NSRunLoop currentRunLoop];  
12.         [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];  
13.         [runLoop run];  
14.     }  
15. }  
16. //創(chuàng)建新的子線程  
17. + (NSThread *)networkRequestThread {  
18.     <span style="white-space:pre">    </span>  static NSThread *_networkRequestThread = nil;  
19.     <span style="white-space:pre">    </span>  static dispatch_once_t oncePredicate;  
20.     <span style="white-space:pre">    </span>  dispatch_once(&oncePredicate, ^{  
21.       <span style="white-space:pre">  </span>  _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];  
22.        <span style="white-space:pre"> </span> [_networkRequestThread start];  
23.     });    
24.     return _networkRequestThread;  
25. }  
26. //初始化NSURLConnection對象  
27. - (instancetype)initWithRequest:(NSURLRequest *)urlRequest {  
28.     NSParameterAssert(urlRequest);  
29.     self = [super init];  
30.     if (!self) {  
31.         return nil;  
32.     }  
33.     _state = AFOperationReadyState;  
34.     self.lock = [[NSRecursiveLock alloc] init];  
35.     self.lock.name = kAFNetworkingLockName;  
36.     self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];  
37.     self.request = urlRequest;    
38.     self.shouldUseCredentialStorage = YES;  
39.     self.securityPolicy = [AFSecurityPolicy defaultPolicy];  
40.     return self;  
41. }  

[objc] view plaincopy

1.  //------------------------線程--------------------  
2.  //線程開始  
3.  - (void)start {  
4.      //加鎖赛惩,保護(hù)線程  
5.      [self.lock lock];  
6.      if ([self isCancelled]) {   //取消線程  
7.          [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];  
8.      } else if ([self isReady]) {    //線程已準(zhǔn)備  
9.          self.state = AFOperationExecutingState;     //將線程調(diào)為執(zhí)行狀態(tài)  
10.           
11.         [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];  
12.     }  
13.     //線程執(zhí)行完畢,解鎖  
14.     [self.lock unlock];  
15. }  
16. //線程已開始  
17. - (void)operationDidStart {  
18.     [self.lock lock];  
19.     if (![self isCancelled]) {  
20.         //創(chuàng)建鏈接對象  
21.         self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];       
22.         //獲取當(dāng)前的NSRunLoop趁餐,用來接收異步回調(diào)事件  
23.         NSRunLoop *runLoop = [NSRunLoop currentRunLoop];  
24.         for (NSString *runLoopMode in self.runLoopModes) {  
25.             //執(zhí)行線程  
26.             [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];  
27.             [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];  
28.         }       
29.         //開始鏈接  
30.         [self.connection start];  
31.     }  
32.     [self.lock unlock];     
33.     //回到主線程喷兼,發(fā)送線程開始通知  
34.     dispatch_async(dispatch_get_main_queue(), ^{  
35.         [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];  
36.     });  
37. }  
38. //線程完成  
39. - (void)finish {  
40.     [self.lock lock];  
41.     self.state = AFOperationFinishedState;  
42.     [self.lock unlock];  
43.     //發(fā)送通知,鏈接完成  
44.     dispatch_async(dispatch_get_main_queue(), ^{  
45.         [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidFinishNotification object:self];  
46.     });  
47. }  
48. //線程取消  
49. - (void)cancel {  
50.     [self.lock lock];  
51.     if (![self isFinished] && ![self isCancelled]) {  
52.         [super cancel];  
53.   
54.         if ([self isExecuting]) {  
55.             [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];  
56.         }  
57.     }  
58.     [self.lock unlock];  
59. }  
60. //取消鏈接  
61. - (void)cancelConnection {  
62.     NSDictionary *userInfo = nil;  
63.     if ([self.request URL]) {  
64.         userInfo = [NSDictionary dictionaryWithObject:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];  
65.     }  
66.     NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];  
67.     if (![self isFinished]) {      //處于鏈接狀態(tài)后雷,取消鏈接  
68.         if (self.connection) {  
69.             //取消鏈接  
70.             [self.connection cancel];  
71.             [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error];  
72.         } else {            //鏈接完成季惯,則直接結(jié)束  
73.             // Accomodate race condition where `self.connection` has not yet been set before cancellation  
74.             self.error = error;  
75.             [self finish];  
76.         }  
77.     }  
78. }  

2.狀態(tài)機(jī)
繼承 NSOperation 有個(gè)很麻煩的東西要處理,就是改變狀態(tài)時(shí)需要發(fā) KVO 通知臀突,否則這個(gè)類加入 NSOperationQueue 不可用了勉抓。 NSOperationQueue 是用 KVO 方式偵聽 NSOperation 狀態(tài)的改變,以判斷這個(gè)任務(wù)當(dāng)前是否已完成候学,完成的任務(wù)需要在隊(duì)列中除去并釋放藕筋。
AFURLConnectionOperation 對此做了個(gè)狀態(tài)機(jī),統(tǒng)一搞定狀態(tài)切換以及發(fā) KVO 通知的問題梳码,內(nèi)部要改變狀態(tài)時(shí)念逞,就只需要類似 self.state = AFOperationReadyState 的調(diào)用而不需要做其他了,狀態(tài)改變的 KVO 通知在 setState 里發(fā)出边翁。
總的來說狀態(tài)管理相關(guān)代碼就三部分,一是限制一個(gè)狀態(tài)可以切換到其他哪些狀態(tài)硕盹,避免狀態(tài)切換混亂符匾,二是狀態(tài) Enum值 與 NSOperation 四個(gè)狀態(tài)方法的對應(yīng),三是在 setState 時(shí)統(tǒng)一發(fā) KVO 通知瘩例。
相關(guān)代碼如下:
[objc] view plaincopy

1.  //-------------------------狀態(tài)機(jī)  
2.  //該方法的作用:狀態(tài) Enum值 與 NSOperation 四個(gè)狀態(tài)方法的對應(yīng)  
3.  static inline NSString * AFKeyPathFromOperationState(AFOperationState state) {  
4.      switch (state) {  
5.          case AFOperationReadyState:  
6.              return @"isReady";  
7.          case AFOperationExecutingState:  
8.              return @"isExecuting";  
9.          case AFOperationFinishedState:  
10.             return @"isFinished";  
11.         case AFOperationPausedState:  
12.             return @"isPaused";  
13.         default: {  
14. #pragma clang diagnostic push  
15. #pragma clang diagnostic ignored "-Wunreachable-code"  
16.             return @"state";  
17. #pragma clang diagnostic pop  
18.         }  
19.     }  
20. }  
21. //NSOperation 狀態(tài)的切換:限制一個(gè)狀態(tài)可以切換到其他哪些狀態(tài)啊胶,避免狀態(tài)切換混亂  
22. static inline BOOL AFStateTransitionIsValid(AFOperationState fromState, AFOperationState toState, BOOL isCancelled) {  
23.     switch (fromState) {  
24.         case AFOperationReadyState:  
25.             switch (toState) {  
26.                 case AFOperationPausedState:  
27.                 case AFOperationExecutingState:  
28.                     return YES;  
29.                 case AFOperationFinishedState:  
30.                     return isCancelled;  
31.                 default:  
32.                     return NO;  
33.             }  
34.         case AFOperationExecutingState:  
35.             switch (toState) {  
36.                 case AFOperationPausedState:  
37.                 case AFOperationFinishedState:  
38.                     return YES;  
39.                 default:  
40.                     return NO;  
41.             }  
42.         case AFOperationFinishedState:  
43.             return NO;  
44.         case AFOperationPausedState:  
45.             return toState == AFOperationReadyState;  
46.         default: {  
47. #pragma clang diagnostic push  
48. #pragma clang diagnostic ignored "-Wunreachable-code"  
49.             switch (toState) {  
50.                 case AFOperationPausedState:  
51.                 case AFOperationReadyState:  
52.                 case AFOperationExecutingState:  
53.                 case AFOperationFinishedState:  
54.                     return YES;  
55.                 default:  
56.                     return NO;  
57.             }  
58.         }  
59. #pragma clang diagnostic pop  
60.     }  
61. }  

[objc] view plaincopy

1.  //-------------------------狀態(tài)機(jī)  
2.  //NSOperationQueue 是用KVO方式偵聽 NSOperation 狀態(tài)的改變  
3.  //在該方法里統(tǒng)一發(fā) KVO 通知給 NSOperationQueue,以判斷這個(gè)任務(wù)當(dāng)前是否已完成垛贤,完成的任務(wù)需要在隊(duì)列中除去并釋放焰坪。  
4.  - (void)setState:(AFOperationState)state {  
5.      if (!AFStateTransitionIsValid(self.state, state, [self isCancelled])) {  
6.          return;  
7.      }    
8.      [self.lock lock];  
9.      NSString *oldStateKey = AFKeyPathFromOperationState(self.state);  
10.     NSString *newStateKey = AFKeyPathFromOperationState(state);  
11.     [self willChangeValueForKey:newStateKey];  
12.     [self willChangeValueForKey:oldStateKey];  
13.     _state = state;  
14.     [self didChangeValueForKey:oldStateKey];  
15.     [self didChangeValueForKey:newStateKey];  
16.     [self.lock unlock];  
17. }  

[objc] view plaincopy

  1. 還有其他相關(guān)代碼:-setState:, -isPaused:, -isReady:, -isExecuting:, -isFinished:.

3.NSURLConnectionDelegate
處理 NSURLConnection Delegate 的內(nèi)容不多,代碼也是按請求回調(diào)的順序排列下去聘惦,十分易讀某饰,主要流程就是接收到響應(yīng)的時(shí)候打開 outputStream,接著有數(shù)據(jù)過來就往 outputStream 寫,在上傳/接收數(shù)據(jù)過程中會回調(diào)上層傳進(jìn)來的相應(yīng)的callback黔漂,在請求完成回調(diào)到 connectionDidFinishLoading 時(shí)诫尽,關(guān)閉 outputStream,用 outputStream 組裝 responseData 作為接收到的數(shù)據(jù)炬守,把 NSOperation 狀態(tài)設(shè)為 finished牧嫉,表示任務(wù)完成,NSOperation 會自動調(diào)用 completeBlock减途,再回調(diào)到上層酣藻。
相關(guān)代碼如下:
[objc] view plaincopy

1.  //使用NSURLConnection時(shí),用該方法檢查證書的有效性  
2.  - (void)connection:(NSURLConnection *)connection  
3.  willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{}  
[objc] view plaincopy
1.  /* 
2.   connection: willSendRequest: redirectResponse: 
3.   這個(gè)方法在請求將要被發(fā)送出去之前會調(diào)用 
4.   返回值是一個(gè)NSURLRequest,就是那個(gè)真正將要被發(fā)送的請求 
5.   第二個(gè)參數(shù)request就是被重定向處理過后的請求 
6.   第三個(gè)參數(shù)<span style="font-family: Arial, Helvetica, sans-serif;">redirectResponse</span><span style="font-family: Arial, Helvetica, sans-serif;">是觸發(fā)重定向請求的響應(yīng)包.默認(rèn)是支持跳轉(zhuǎn)的鳍置。</span> 
7.   */  
8.  - (NSURLRequest *)connection:(NSURLConnection *)connection  
9.               willSendRequest:(NSURLRequest *)request  
10.             redirectResponse:(NSURLResponse *)redirectResponse  
11. {  
12.     if (self.redirectResponse) {  
13.         return self.redirectResponse(connection, request, redirectResponse);  
14.     } else {  
15.         return request;  
16.     }  
17. }  
18. //上傳數(shù)據(jù)  
19. - (void)connection:(NSURLConnection __unused *)connection  
20.    didSendBodyData:(NSInteger)bytesWritten  
21.  totalBytesWritten:(NSInteger)totalBytesWritten  
22. totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite  
23. {  
24.     //上傳數(shù)據(jù)過程中回調(diào)上層傳進(jìn)來的相應(yīng)的callback  
25.     if (self.uploadProgress) {  
26.         dispatch_async(dispatch_get_main_queue(), ^{  
27.             self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);  
28.         });  
29.     }  
30. }  
31. //代理對象接收到響應(yīng)的時(shí)候打開 outputStream  
32. - (void)connection:(NSURLConnection __unused *)connection  
33. didReceiveResponse:(NSURLResponse *)response  
34. {  
35.     self.response = response;   
36.     [self.outputStream open];  
37. }  
38. //有數(shù)據(jù)過來時(shí)辽剧,往outputStream寫數(shù)據(jù)  
39. - (void)connection:(NSURLConnection __unused *)connection  
40.     didReceiveData:(NSData *)data  
41. {  
42.     NSUInteger length = [data length];  
43.     while (YES) {  
44.         NSInteger totalNumberOfBytesWritten = 0;  
45.         if ([self.outputStream hasSpaceAvailable]) {  
46.             const uint8_t *dataBuffer = (uint8_t *)[data bytes];  
47.             NSInteger numberOfBytesWritten = 0;  
48.             while (totalNumberOfBytesWritten < (NSInteger)length) {  
49.                 numberOfBytesWritten = [self.outputStream write:&dataBuffer[(NSUInteger)totalNumberOfBytesWritten] maxLength:(length - (NSUInteger)totalNumberOfBytesWritten)];  
50.                 if (numberOfBytesWritten == -1) {  
51.                     break;  
52.                 }             
53.                 totalNumberOfBytesWritten += numberOfBytesWritten;  
54.             }  
55.             break;  
56.         }       
57.         if (self.outputStream.streamError) {  
58.             [self.connection cancel];  
59.             [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:self.outputStream.streamError];  
60.             return;  
61.         }  
62.     }    
63.     //接收數(shù)據(jù)過程中會回調(diào)上層傳進(jìn)來的相應(yīng)的callback  
64.     dispatch_async(dispatch_get_main_queue(), ^{  
65.         self.totalBytesRead += (long long)length;  
66.   
67.         if (self.downloadProgress) {  
68.             self.downloadProgress(length, self.totalBytesRead, self.response.expectedContentLength);  
69.         }  
70.     });  
71. }  
72. //請求完成回調(diào)到 connectionDidFinishLoading 時(shí),關(guān)閉 outputStream  
73. //用 outputStream 組裝 responseData 作為接收到的數(shù)據(jù)墓捻,把 NSOperation 狀態(tài)設(shè)為 finished抖仅,表示任務(wù)完成  
74. - (void)connectionDidFinishLoading:(NSURLConnection __unused *)connection {  
75.     self.responseData = [self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey];  
76.     [self.outputStream close];  
77.     if (self.responseData) {  
78.        self.outputStream = nil;  
79.     }  
80.     self.connection = nil;  
81.     [self finish];  
82. }  

4.setCompleteBlock
AFNetworking重寫NSOperation提供的setCompletionBlock,用于任務(wù)完成時(shí)回調(diào)傳進(jìn)來的block砖第,并且實(shí)現(xiàn)消除循環(huán)引用撤卢。
在 NSOperation 的實(shí)現(xiàn)里,completionBlock 是 NSOperation 對象的一個(gè)成員梧兼,NSOperation 對象持有著 completionBlock放吩,若傳進(jìn)來的 block 用到了 NSOperation 對象,或者 block 用到的對象持有了這個(gè) NSOperation 對象羽杰,就會造成循環(huán)引用渡紫。這里執(zhí)行完 block 后調(diào)用 [strongSelf setCompletionBlock:nil] 把 completionBlock 設(shè)成 nil,手動釋放 self(NSOperation對象) 持有的 completionBlock 對象考赛,打破循環(huán)引用惕澎。
相關(guān)代碼如下:
[objc] view plaincopy

1.  <span style="font-size:14px;">//任務(wù)完成,回調(diào)block  
2.  - (void)setCompletionBlock:(void (^)(void))block {  
3.      [self.lock lock];  
4.      if (!block) {  
5.          [super setCompletionBlock:nil];  
6.      } else {  
7.          //weakSelf是為了block不持有self颜骤,避免循環(huán)引用  
8.          __weak __typeof(self)weakSelf = self;  
9.          [super setCompletionBlock:^ {  
10.             //再聲明一個(gè)strongSelf是因?yàn)橐坏┻M(jìn)入block執(zhí)行唧喉,就不允許self在這個(gè)執(zhí)行過程中釋放。block執(zhí)行完后這個(gè)strongSelf會自動釋放忍抽,沒有循環(huán)引用問題八孝。  
11.             __strong __typeof(weakSelf)strongSelf = weakSelf;  
12. #pragma clang diagnostic push  
13. #pragma clang diagnostic ignored "-Wgnu"  
14.             dispatch_group_t group = strongSelf.completionGroup ?: url_request_operation_completion_group();  
15.             dispatch_queue_t queue = strongSelf.completionQueue ?: dispatch_get_main_queue();  
16. #pragma clang diagnostic pop  
17.             dispatch_group_async(group, queue, ^{  
18.                 //傳入一個(gè) block 作為任務(wù)執(zhí)行完成時(shí)(state狀態(tài)機(jī)變?yōu)閒inished時(shí))的回調(diào)  
19.                 block();  
20.             });  
21.             /* 
22.              循環(huán)引用:NSOperation持有completionBlock,若傳進(jìn)來的block 用到了 NSOperation 對象鸠项, 
23.              或者 block 用到的對象持有了這個(gè) NSOperation 對象干跛,就會造成循環(huán)引用 
24.              解決方法: 
25.              消除循環(huán)引用,手動釋放 self(NSOperation對象) 持有的 completionBlock 對象,打破循環(huán)引用 
26.              */  
27.             dispatch_group_notify(group, url_request_operation_completion_queue(), ^{  
28.                 [strongSelf setCompletionBlock:nil];  
29.             });  
30.         }];  
31.     }  
32.     [self.lock unlock];  
33. }</span>  

5.batchOfRequestOperations
這里額外提供了一個(gè)便捷接口祟绊,可以傳入一組請求楼入,在所有請求完成后回調(diào) complionBlock哥捕,在每一個(gè)請求完成時(shí)回調(diào) progressBlock 通知外面有多少個(gè)請求已完成
[objc] view plaincopy

1.  <span style="font-size:14px;">+ (NSArray *)batchOfRequestOperations:(NSArray *)operations  
2.                          progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock  
3.                        completionBlock:(void (^)(NSArray *operations))completionBlock  
4.  {  
5.      //請求不存在,或者請求數(shù)為0浅辙,返回  
6.      if (!operations || [operations count] == 0) {  
7.          return @[[NSBlockOperation blockOperationWithBlock:^{  
8.              dispatch_async(dispatch_get_main_queue(), ^{  
9.                  if (completionBlock) {  
10.                     completionBlock(@[]);  
11.                 }  
12.             });  
13.         }]];  
14.     }  
15.     __block dispatch_group_t group = dispatch_group_create();  
16.     //任務(wù)數(shù)為0時(shí)執(zhí)行dispatch_group_notify的內(nèi)容  
17.     NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{  
18.         dispatch_group_notify(group, dispatch_get_main_queue(), ^{  
19.             if (completionBlock) {  
20.                 completionBlock(operations);  
21.             }  
22.         });  
23.     }];  
24.     //取出每一個(gè)請求(任務(wù))  
25.     for (AFURLConnectionOperation *operation in operations) {  
26.         operation.completionGroup = group;  
27.         void (^originalCompletionBlock)(void) = [operation.completionBlock copy];  
28.         __weak __typeof(operation)weakOperation = operation;  
29.         operation.completionBlock = ^{  
30.             __strong __typeof(weakOperation)strongOperation = weakOperation;  
31. #pragma clang diagnostic push  
32. #pragma clang diagnostic ignored "-Wgnu"  
33.             dispatch_queue_t queue = strongOperation.completionQueue ?: dispatch_get_main_queue();  
34. #pragma clang diagnostic pop  
35.             //異步執(zhí)行任務(wù)  
36.             dispatch_group_async(group, queue, ^{  
37.                 if (originalCompletionBlock) {  
38.                     //任務(wù)完成后回調(diào)block  
39.                     originalCompletionBlock();  
40.                 }  
41.                 NSUInteger numberOfFinishedOperations = [[operations indexesOfObjectsPassingTest:^BOOL(id op, NSUInteger __unused idx,  BOOL __unused *stop) {  
42.                     return [op isFinished];  
43.                 }] count];  
44.   
45.                 //在每一個(gè)請求完成時(shí)回調(diào) progressBlock 通知外面有多少個(gè)請求已完成扭弧。  
46.                 if (progressBlock) {  
47.                     progressBlock(numberOfFinishedOperations, [operations count]);  
48.                 }  
49.   
50.                 dispatch_group_leave(group);        //類似release,任務(wù)數(shù)-1  
51.             });  
52.         };  
53.         dispatch_group_enter(group);                //類似retain记舆,任務(wù)數(shù)+1  
54.         [batchedOperation addDependency:operation];  
55.     }  
56.     return [operations arrayByAddingObject:batchedOperation];  
57. }</span>  

6.鎖鸽捻、序列化、backgroundTask
鎖 :AFURLConnectionOperation 有一把遞歸鎖泽腮,在所有會訪問/修改成員變量的對外接口都加了鎖御蒲,因?yàn)檫@些對外的接口用戶是可以在任意線程調(diào)用的,對于訪問和修改成員變量的接口诊赊,必須用鎖保證線程安全厚满。
序列化:AFNetworking 的多數(shù)類都支持序列化,實(shí)現(xiàn)的是 NSSecureCoding 的接口碧磅,用 -decodeObjectOfClass:forKey: 方法碘箍,指定 Class保證序列化后的數(shù)據(jù)不被篡改,若不指定 Class鲸郊,-decode 出來的對象可能不是原來的對象丰榴,有潛在風(fēng)險(xiǎn)。
backgroundTask:這里提供了setShouldExecuteAsBackgroundTaskWithExpirationHandler 接口秆撮,決定APP進(jìn)入后臺后是否繼續(xù)發(fā)送接收請求四濒,并在后臺執(zhí)行時(shí)間超時(shí)后取消所有請求。在 dealloc 里需要調(diào)用 [application endBackgroundTask:] 职辨,告訴系統(tǒng)這個(gè)后臺任務(wù)已經(jīng)完成盗蟆,不然系統(tǒng)會一直讓你的APP運(yùn)行在后臺,直到超時(shí)舒裤。

7.AFHTTPRequestOperation

AFHTTPRequestOperation 繼承了 AFURLConnectionOperation喳资,實(shí)現(xiàn)的功能比較少,主要多了responseSerializer腾供,暫停下載斷點(diǎn)續(xù)傳骨饿,以及提供接口請求成功失敗的回調(diào)接口。
理解了AFURLConnectionOperation台腥,就會覺得AFHTTPRequestOperation比較簡單,所以具體的就不寫了绒北。
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation,idresponseObject))success
                              failure:(void (^)(AFHTTPRequestOperation *operation,NSError *error))failure;

8.AFHTTPRequestOperationManager

AFHTTPRequestOperationManager封裝了AFNetworking其他功能的各個(gè)模塊黎侈,如AFHTTPRequestSerializer(請求序列化),
AFHTTPResponseSerializer(響應(yīng)序列化)闷游,
AFSecurityPolicy(安全策略)峻汉,
AFNetworkReachabilityManager(可達(dá)性)贴汪,
封裝了HTTP請求所相關(guān)的代碼
并且將所有的請求添加到同一個(gè)NSOperationQueue請求隊(duì)列里。
并且封裝 HTTP 請求的常見方式休吠,GET / POST / PUT / DELETE / PATCH……

NSURLConnection相關(guān)的代碼就解析到這里扳埂。下一篇講AFNetworking其他功能模塊!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瘤礁,一起剝皮案震驚了整個(gè)濱河市阳懂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌柜思,老刑警劉巖岩调,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赡盘,居然都是意外死亡号枕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門陨享,熙熙樓的掌柜王于貴愁眉苦臉地迎上來葱淳,“玉大人,你說我怎么就攤上這事抛姑≡薏蓿” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵途戒,是天一觀的道長坑傅。 經(jīng)常有香客問我,道長喷斋,這世上最難降的妖魔是什么唁毒? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮星爪,結(jié)果婚禮上浆西,老公的妹妹穿的比我還像新娘。我一直安慰自己顽腾,他們只是感情好近零,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抄肖,像睡著了一般久信。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上漓摩,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天裙士,我揣著相機(jī)與錄音,去河邊找鬼管毙。 笑死腿椎,一個(gè)胖子當(dāng)著我的面吹牛桌硫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播啃炸,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼铆隘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了南用?” 一聲冷哼從身側(cè)響起膀钠,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎训枢,沒想到半個(gè)月后托修,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恒界,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年睦刃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片十酣。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡涩拙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耸采,到底是詐尸還是另有隱情兴泥,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布虾宇,位于F島的核電站搓彻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嘱朽。R本人自食惡果不足惜旭贬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搪泳。 院中可真熱鬧稀轨,春花似錦、人聲如沸岸军。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽艰赞。三九已至佣谐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間方妖,已是汗流浹背台谍。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人趁蕊。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像仔役,于是被迫代替她去往敵國和親掷伙。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容