[无限互联]AFNetworking核心类AFURLConnectionOperation的详解

发布时间:2014-12-03 09:51:08   来源:文档文库   
字号:

上一篇我们了解了AFNetworking各个模块的功能,今天我们来了解一下AFNetworking源码的实现

我们先看看AFURLConnectionOperation   作为AFNetworking最核心的类到底有什么神奇之处!

1.线程

先来看看 NSURLConnection 发送请求时的线程情况,NSURLConnection 是被设计成异步发送的,调用了start方法后,NSURLConnection 会新建一些线程用底层的 CFSocket 去发送和接收请求,在发送和接收的一些事件发生后通知原来线程的Runloop去回调事件。

NSURLConnection 的同步方法 sendSynchronousRequest 方法也是基于异步的,同样要在其他线程去处理请求的发送和接收,只是同步方法会手动block住线程,发送状态的通知也不是通过 RunLoop 进行。

NSURLConnection发送有以下三种方式

在主线程调异步接口

在子线程调同步接口

在子线程调异步接口

AFNetworking使用的是最后一种方式方法,AFNetworking内部相关线程大致的关系如下图所示

NSURLConnection是一个系统控件,所以我们可以把NSURLConnection当做一个黑盒,只管它的 start callback 就行了。如果使用 AFHttpRequestOperationManager 的接口发送请求,这些请求会统一在一个 NSOperationQueue 里去发,所以多了上面 NSOperationQueue 的一个线程。

相关代码:

[objc] view plaincopy

1. //-------------------------线程--------------------  

2. /* 

3.     子线程调用异步接口,子线程需要有 Runloop 去接收异步回调事件,这里也可以每个请求都新建一条 

4.     带有 Runloop 的线程去侦听回调,但这一点好处都没有,既然是异步回调,除了处理回调内容,其他 

5.     时间线程都是空闲可利用的,所有请求共用一个响应的线程就够了。 

6.  */  

7. //获取当前的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. //创建新的子线程  

17. + (NSThread *)networkRequestThread {  

18.     "white-space:pre">      static NSThread *_networkRequestThread = nil;  

19.     "white-space:pre">      static dispatch_once_t oncePredicate;  

20.     "white-space:pre">      dispatch_once(&oncePredicate, ^{  

21.       "white-space:pre">    _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];  

22.        "white-space:pre" [_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.     //加锁,保护线程  

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]) {    //线程已准备  

9.         self.state = AFOperationExecutingState;     //将线程调为执行状态  

10.           

11.         [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];  

12.     }  

13.     //线程执行完毕,解锁  

14.     [self.lock unlock];  

15. }  

16. //线程已开始  

17. - (void)operationDidStart {  

18.     [self.lock lock];  

19.     if (![self isCancelled]) {  

20.         //创建链接对象  

21.         self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];       

22.         //获取当前的NSRunLoop,用来接收异步回调事件  

23.         NSRunLoop *runLoop = [NSRunLoop currentRunLoop];  

24.         for (NSString *runLoopMode in self.runLoopModes) {  

25.             //执行线程  

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.     //回到主线程,发送线程开始通知  

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.     //发送通知,链接完成  

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]) {      //处于链接状态,取消链接  

68.         if (self.connection) {  

69.             //取消链接  

70.             [self.connection cancel];  

71.             [self performSelector:@selector(connection:didFailWithError:) withObject:self.connection withObject:error];  

72.         } else {            //链接完成,则直接结束  

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.状态机

继承 NSOperation 有个很麻烦的东西要处理,就是改变状态时需要发 KVO 通知,否则这个类加入 NSOperationQueue 不可用了。 NSOperationQueue 是用 KVO 方式侦听 NSOperation 状态的改变,以判断这个任务当前是否已完成,完成的任务需要在队列中除去并释放。

AFURLConnectionOperation 对此做了个状态机,统一搞定状态切换以及发 KVO 通知的问题,内部要改变状态时,就只需要类似 self.state = AFOperationReadyState 的调用而不需要做其他了,状态改变的 KVO 通知在 setState 里发出。

总的来说状态管理相关代码就三部分,一是限制一个状态可以切换到其他哪些状态,避免状态切换混乱,二是状态 Enum NSOperation 四个状态方法的对应,三是在 setState 时统一发 KVO 通知。

相关代码如下:

[objc] view plaincopy

1. //-------------------------状态机  

2. //该方法的作用:状态 Enum值 与 NSOperation 四个状态方法的对应  

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 状态的切换:限制一个状态可以切换到其他哪些状态,避免状态切换混乱  

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. //-------------------------状态机  

2. //NSOperationQueue 是用KVO方式侦听 NSOperation 状态的改变  

3. //在该方法里统一发 KVO 通知给 NSOperationQueue,以判断这个任务当前是否已完成,完成的任务需要在队列中除去并释放。  

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. 还有其他相关代码:-setState:, -isPaused:, -isReady:, -isExecuting:, -isFinished:.  

3.NSURLConnectionDelegate

处理 NSURLConnection Delegate 的内容不多,代码也是按请求回调的顺序排列下去,十分易读,主要流程就是接收到响应的时候打开 outputStream,接着有数据过来就往 outputStream 写,在上传/接收数据过程中会回调上层传进来的相应的callback,在请求完成回调到 connectionDidFinishLoading 时,关闭 outputStream,用 outputStream 组装 responseData 作为接收到的数据,把 NSOperation 状态设为 finished,表示任务完成,NSOperation 会自动调用 completeBlock,再回调到上层。

相关代码如下:

[objc] view plaincopy

1. //使用NSURLConnection时,用该方法检查证书的有效性  

2. - (void)connection:(NSURLConnection *)connection  

3. willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{}  

[objc] view plaincopy

1. /* 

2.  connection: willSendRequest: redirectResponse: 

3.  这个方法在请求将要被发送出去之前会调用 

4.  返回值是一个NSURLRequest,就是那个真正将要被发送的请求 

5.  第二个参数request就是被重定向处理过后的请求 

6.  第三个参数redirectResponse是触发重定向请求的响应包.默认是支持跳转的。 

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. //上传数据  

19. - (void)connection:(NSURLConnection __unused *)connection  

20.    didSendBodyData:(NSInteger)bytesWritten  

21.  totalBytesWritten:(NSInteger)totalBytesWritten  

22. totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite  

23. {  

24.     //上传数据过程中回调上层传进来的相应的callback  

25.     if (self.uploadProgress) {  

26.         dispatch_async(dispatch_get_main_queue(), ^{  

27.             self.uploadProgress((NSUInteger)bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);  

28.         });  

29.     }  

30. }  

31. //代理对象接收到响应的时候打开 outputStream  

32. - (void)connection:(NSURLConnection __unused *)connection  

33. didReceiveResponse:(NSURLResponse *)response  

34. {  

35.     self.response = response;   

36.     [self.outputStream open];  

37. }  

38. //有数据过来时,往outputStream写数据  

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.     //接收数据过程中会回调上层传进来的相应的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. //请求完成回调到 connectionDidFinishLoading 时,关闭 outputStream  

73. //用 outputStream 组装 responseData 作为接收到的数据,把 NSOperation 状态设为 finished,表示任务完成  

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,用于任务完成时回调传进来的block,并且实现消除循环引用。

NSOperation 的实现里,completionBlock NSOperation 对象的一个成员,NSOperation 对象持有着 completionBlock,若传进来的 block 用到了 NSOperation 对象,或者 block 用到的对象持有了这个 NSOperation 对象,就会造成循环引用。这里执行完 block 后调用 [strongSelf setCompletionBlock:nil] completionBlock 设成 nil,手动释放 self(NSOperation对象) 持有的 completionBlock 对象,打破循环引用。

相关代码如下:

[objc] view plaincopy

1. "font-size:14px;">//任务完成,回调block  

2. - (void)setCompletionBlock:(void (^)(void))block {  

3.     [self.lock lock];  

4.     if (!block) {  

5.         [super setCompletionBlock:nil];  

6.     } else {  

7.         //weakSelf是为了block不持有self,避免循环引用  

8.         __weak __typeof(self)weakSelf = self;  

9.         [super setCompletionBlock:^ {  

10.             //再声明一个strongSelf是因为一旦进入block执行,就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。  

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.                 //传入一个 block 作为任务执行完成时(state状态机变为finished时)的回调  

19.                 block();  

20.             });  

21.             /* 

22.              循环引用:NSOperation持有completionBlock,若传进来的block 用到了 NSOperation 对象, 

23.              或者 block 用到的对象持有了这个 NSOperation 对象,就会造成循环引用 

24.              解决方法: 

25.              消除循环引用,手动释放 self(NSOperation对象) 持有的 completionBlock 对象,打破循环引用 

26.              */  

27.             dispatch_group_notify(group, url_request_operation_completion_queue(), ^{  

28.                 [strongSelf setCompletionBlock:nil];  

29.             });  

30.         }];  

31.     }  

32.     [self.lock unlock];  

33. }  

5.batchOfRequestOperations

这里额外提供了一个便捷接口,可以传入一组请求,在所有请求完成后回调 complionBlock,在每一个请求完成时回调 progressBlock 通知外面有多少个请求已完成

[objc] view plaincopy

1. "font-size:14px;">+ (NS *)batchOfRequestOperations:(NS *)operations  

2.                         progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock  

3.                       completionBlock:(void (^)(NS *operations))completionBlock  

4. {  

5.     //请求不存在,或者请求数为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.     //任务数为0时执行dispatch_group_notify的内容  

17.     NSBlockOperation *batchedOperation = [NSBlockOperation blockOperationWithBlock:^{  

18.         dispatch_group_notify(group, dispatch_get_main_queue(), ^{  

19.             if (completionBlock) {  

20.                 completionBlock(operations);  

21.             }  

22.         });  

23.     }];  

24.     //取出每一个请求(任务)  

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.             //异步执行任务  

36.             dispatch_group_async(group, queue, ^{  

37.                 if (originalCompletionBlock) {  

38.                     //任务完成后回调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.                 //在每一个请求完成时回调 progressBlock 通知外面有多少个请求已完成。  

46.                 if (progressBlock) {  

47.                     progressBlock(numberOfFinishedOperations, [operations count]);  

48.                 }  

49.   

50.                 dispatch_group_leave(group);        //类似release,任务数-1  

51.             });  

52.         };  

53.         dispatch_group_enter(group);                //类似retain,任务数+1  

54.         [batchedOperation addDependency:operation];  

55.     }  

56.     return [operations arrayByAddingObject:batchedOperation];  

57. }  

6.锁、序列化、backgroundTask

AFURLConnectionOperation 有一把递归锁,在所有会访问/修改成员变量的对外接口都加了锁,因为这些对外的接口用户是可以在任意线程调用的,对于访问和修改成员变量的接口,必须用锁保证线程安全。

序列化:AFNetworking 的多数类都支持序列化,实现的是 NSSecureCoding 的接口,用 -decodeObjectOfClass:forKey: 方法,指定 Class保证序列化后的数据不被篡改,若不指定 Class-decode 出来的对象可能不是原来的对象,有潜在风险。

backgroundTask这里提供了setShouldExecuteAsBackgroundTaskWithExpirationHandler 接口,决定APP进入后台后是否继续发送接收请求,并在后台执行时间超时后取消所有请求。在 dealloc 里需要调用 [application endBackgroundTask:] ,告诉系统这个后台任务已经完成,不然系统会一直让你的APP运行在后台,直到超时。

7.AFHTTPRequestOperation

AFHTTPRequestOperation 继承了 AFURLConnectionOperation,实现的功能比较少,主要多了responseSerializer,暂停下载断点续传,以及提供接口请求成功失败的回调接口。理解了AFURLConnectionOperation,就会觉得AFHTTPRequestOperation比较简单,所以具体的就不写了。

- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation,idresponseObject))success

                              failure:(void (^)(AFHTTPRequestOperation *operation,NSError *error))failure;

8.AFHTTPRequestOperationManager

AFHTTPRequestOperationManager封装了AFNetworking其他功能的各个模块,如AFHTTPRequestSerializer(请求序列化),AFHTTPResponseSerializer(响应序列化),AFSecurityPolicy(安全策略),AFNetworkReachabilityManager(可达性),封装了HTTP请求所相关的代码

并且将所有的请求添加到同一个NSOperationQueue请求队列里。

并且封装 HTTP 请求的常见方式,GET / POST / PUT / DELETE / PATCH……



NSURLConnection相关的代码就解析到这里。下一篇讲AFNetworking其他功能模块!

本文来源:https://www.2haoxitong.net/k/doc/9ab1c6f8daef5ef7ba0d3ca8.html

《[无限互联]AFNetworking核心类AFURLConnectionOperation的详解.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式