分享

AFNetWorking的理解

 没原创_去搜索 2015-08-11

先说点题外话,从事ios开发一年,学习了苹果的好多框架,对第三方库也学习了不少,从ios6到ios8,苹果对其库的修改和完善也越来越“大而全”,当然也保留了集成度较低的api,先说说大而全api的好处,用起来很方便,不用你关心内部实现,直接在你的业务逻辑代码中使用他,但是当你根据你自己的业务逻辑进行优化的时候就没办法了,相反集成度较低的api灵活度就好的多,你可以采用自己的独特方法进行管理和使用,当然你付出的代价是自己写好多代码。有人会问,既然使用者多有自己的业务逻辑,干嘛还要使用大而全的api,其实大而全只是一个相对的概念,库的开发者会根据大多数的使用场景去继承api,那么大部分的应用场景是非常满足的,而且继承的api会提供可配置的参数,总之使用库之前根据场景选择合适的api。

关于AFNetWoring,从事iOS开发的几乎没人不知道,这个库是在NSURLConnection 和 NSURLSession的基础上进行封装的,逻辑简单清楚,设计思想很好。

1.NSURLConnection中的AF

NSRULConnection是苹果IOS2.0之后推出的用于加载URL资源的API,其使用很简单,分为同步和异步

异步

  1. NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"wwww.baidu.com"]];  
  2. NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];  

  1. NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"wwww.baidu.com"]];  
  2. [NSURLConnection sendAsynchronousRequest:request   
  1. <span style="white-space:pre">                </span>       queue:[NSOperationQueue mainQueue]  
  1. <span style="white-space:pre">            </span>   completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {  
  2.           
  3.     }]  
 同步
  1. NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"wwww.baidu.com"]];  
  2. NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];  

解释一下,对于一个URLConnection需要一个URLRequest作为参数,然后才能load

其实同步和异步方法底层实现的本质是一样,都是告诉系统请求一个URL资源,系统会有自己私有线程去load,然后load完成后告诉调用者。而同步方法是,阻塞调用线程,然后一直等待,直到load完成,一般不会在主线程调用同步方法,这样会阻塞用户交互。异步方法是调用之后就不管了,系统加载完成后会通知调用者,通知调用者的方式有两种,一,代理方式, 系统会在connection的调用线程通知代理。二是queue方式,系统会在调用者指定的queue中通知调用者。

AFnetworing利用的是异步加载数据的方式,而且是代理的方式。首先我们来做加法讲解。

1,首先一个请求的调用必须有一个线程来执行,那么让哪个线程来做,主线程,还是其他线程,一般情况下没人用主线程,因为app中同时请求很多东西,都放在主线程里面去做那么付出的代价时主线程会各种卡顿,代理的各种交互都得让主线程去处理。那么就得用子线程。

用子线程做,创建一个NSthread,执行一个NSURLConnection,然后你得指定一个代理,代理是一个object实例,而且这个object必须拥护存储加载数据的功能,整合后就是模型1.   NSThread + connection + delegateObject;

有问题吗,有,线程并发控制难以控制,那么我们就如何更改这个呢,将NStread 换成NSOperation的子类,然后用NSOperation的子类替换delegateObject,那么这样还有一个问题,NSOperation如何控制并发呢,所以我们还缺少NSOpreationQueue。NSOpreationQueue来管理NSOperation,这样并发就成功了。

整理后为模型2 NSOperation + connnetion + NSOperationQueue

模型2已经很好了,但是还有一个问题,就是 NSOperationQueue在分配一个线程去执行connection,然后load完成后,在后通知这个线程,并调用代理函数执行任务,但是一般情况下,load过程比较慢,系统中会停留一些 NSOperation执行线程,并且什么任务都不做,就是等待,所以很浪费。所以,我们这样考虑,当 NSOperationQueue分配一个线程执行NSoperation的时候我们在执行时候,让NSoperation去通知一个共有的线程去执行NSoperation的加载方法,那么NSURLConnection在加载完成后会通知调用线程,这个时候就是这个共有线程,那么其他线程就执行完成然后得以释放解脱,只有一个共有线程在忙碌,这样就不会有浪费。ok第三种模型出现了

NSOperation + connnetion + NSOperationQueue + COMMENThread

模型三就是AF采用设计方法。

AF 中 NSURLConnection的工作流程代码给出源码

我们需要请求一个网络资源,我们调用AFHTTPRequestOperationManager中的方法

  1. - (AFHTTPRequestOperation *)GET:(NSString *)URLString  
  2.                      parameters:(id)parameters  
  3.                         success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success  
  4.                         failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure  
  5. {  
  6.     NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];  
  7.     AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];  
  8.   
  9.     [self.operationQueue addOperation:operation];  
  10.   
  11.     return operation;  
  12. }  

  1. - (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request  
  2.                                                     success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success  
  3.                                                     failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure  
  4. {  
  5.     AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];  
  6.     operation.responseSerializer = self.responseSerializer;  
  7.     operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage;  
  8.     operation.credential = self.credential;  
  9.     operation.securityPolicy = self.securityPolicy;  
  10.   
  11.     [operation setCompletionBlockWithSuccess:success failure:failure];  
  12.     operation.completionQueue = self.completionQueue;  
  13.     operation.completionGroup = self.completionGroup;  
  14.   
  15.     return operation;  
  16. }  

  1. - (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success  
  2.                               failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure  
  3. {  
  4.     // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.  
  5. #pragma clang diagnostic push  
  6. #pragma clang diagnostic ignored "-Warc-retain-cycles"  
  7. #pragma clang diagnostic ignored "-Wgnu"  
  8.     self.completionBlock = ^{  
  9.         if (self.completionGroup) {  
  10.             dispatch_group_enter(self.completionGroup);  
  11.         }  
  12.   
  13.         dispatch_async(http_request_operation_processing_queue(), ^{  
  14.             if (self.error) {  
  15.                 if (failure) {  
  16.                     dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{  
  17.                         failure(selfself.error);  
  18.                     });  
  19.                 }  
  20.             } else {  
  21.                 id responseObject = self.responseObject;  
  22.                 if (self.error) {  
  23.                     if (failure) {  
  24.                         dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{  
  25.                             failure(selfself.error);  
  26.                         });  
  27.                     }  
  28.                 } else {  
  29.                     if (success) {  
  30.                         dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{  
  31.                             success(self, responseObject);  
  32.                         });  
  33.                     }  
  34.                 }  
  35.             }  
  36.   
  37.             if (self.completionGroup) {  
  38.                 dispatch_group_leave(self.completionGroup);  
  39.             }  
  40.         });  
  41.     };  
  42. #pragma clang diagnostic pop  
  43. }  

这个方法是一个为get的网络请求,AFHTTPRequestOperationManager生成一个AFHTTPRequestOperation,在AFHTTPRequestOperation的构造过程中调用

  1. - (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success  
  2.                               failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure  
添加完成块。生成完成后加入到自己的operationQueue中在合适的时机分配线程给AFHTTPRequestOperation,调用AFHTTPRequestOperation的start方法

  1. - (void)start {  
  2.     [self.lock lock];  
  3.     if ([self isCancelled]) {  
  4.         [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];  
  5.     } else if ([self isReady]) {  
  6.         self.state = AFOperationExecutingState;  
  7.           
  8.         [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];  
  9.     }  
  10.     [self.lock unlock];  
  11. }  
start中调用了下面这个函数
  1. [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];  
这个是中有一个
  1. [[self class] networkRequestThread]  

这个是干什么的

  1. + (NSThread *)networkRequestThread {  
  2.     static NSThread *_networkRequestThread = nil;  
  3.     static dispatch_once_t oncePredicate;  
  4.     dispatch_once(&oncePredicate, ^{  
  5.         _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];  
  6.         [_networkRequestThread start];  
  7.     });  
  8.       
  9.     return _networkRequestThread;  
  10. }  

这是AFHTTPRequestOperation中的类方法,也就是说调用多次都会只执行一次,产生了一个共有线程 来看AFHTTPRequestOperation让这个共有线程operationDidStart
  1. - (void)operationDidStart {  
  2.     [self.lock lock];  
  3.     if (![self isCancelled]) {  
  4.         self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];  
  5.           
  6.         NSRunLoop *runLoop = [NSRunLoop currentRunLoop];  
  7.         for (NSString *runLoopMode in self.runLoopModes) {  
  8.             [self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];  
  9.             [self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];  
  10.         }  
  11.           
  12.         [self.connection start];  
  13.     }  
  14.     [self.lock unlock];  
  15.       
  16.     dispatch_async(dispatch_get_main_queue(), ^{  
  17.         [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];  
  18.     });  
  19. }  

这个共有线程创建了一个NSURLConnection,然后异步发送请求,那么NSURLConnection的代理回调也会在这个线程中执行,那么多个网络请求实际上都是让这个共有线程发送,然后接受,一个线程来完成多个发送和接受。这里有个问题,请求过多的话是否有问题,一个线程忙的过来吗,答案是可以的,因为都是异步,而且无阻塞。最后完成加载后,执行operation的完成块,执行完成块的线程是系统的一个线程。

思考一下,从并发性的角度来看,AF并没有做什么,根本的并发还是系统底层对NSURLConnection的并发控制, 所谓operationQueue的并发是干什么用的,那要是我仅仅用一个线程来收发,并且没个请求都有相应的存储数据的数据结构和完成块就ok了,干嘛要用queue和NSOperation,既然AF用了就有用,因为他们是因为NSOperation可以cancel,可以暂停,等等,而且这些控制都由queue去管理,这样我们自己写的管理代码就少的多,而且我们管理线程肯定不能优于系统,所以,AF这种设计是非常合理的。



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多