先说点题外话,从事ios开发一年,学习了苹果的好多框架,对第三方库也学习了不少,从ios6到ios8,苹果对其库的修改和完善也越来越“大而全”,当然也保留了集成度较低的api,先说说大而全api的好处,用起来很方便,不用你关心内部实现,直接在你的业务逻辑代码中使用他,但是当你根据你自己的业务逻辑进行优化的时候就没办法了,相反集成度较低的api灵活度就好的多,你可以采用自己的独特方法进行管理和使用,当然你付出的代价是自己写好多代码。有人会问,既然使用者多有自己的业务逻辑,干嘛还要使用大而全的api,其实大而全只是一个相对的概念,库的开发者会根据大多数的使用场景去继承api,那么大部分的应用场景是非常满足的,而且继承的api会提供可配置的参数,总之使用库之前根据场景选择合适的api。
关于AFNetWoring,从事iOS开发的几乎没人不知道,这个库是在NSURLConnection 和 NSURLSession的基础上进行封装的,逻辑简单清楚,设计思想很好。
1.NSURLConnection中的AF
NSRULConnection是苹果IOS2.0之后推出的用于加载URL资源的API,其使用很简单,分为同步和异步
异步
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"wwww.baidu.com"]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"wwww.baidu.com"]];
[NSURLConnection sendAsynchronousRequest:request
<span style="white-space:pre"> </span> queue:[NSOperationQueue mainQueue]
<span style="white-space:pre"> </span> completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
}]
同步
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"wwww.baidu.com"]];
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中的方法
- (AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure];
[self.operationQueue addOperation:operation];
return operation;
}
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = self.responseSerializer;
operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage;
operation.credential = self.credential;
operation.securityPolicy = self.securityPolicy;
[operation setCompletionBlockWithSuccess:success failure:failure];
operation.completionQueue = self.completionQueue;
operation.completionGroup = self.completionGroup;
return operation;
}
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^{
if (self.completionGroup) {
dispatch_group_enter(self.completionGroup);
}
dispatch_async(http_request_operation_processing_queue(), ^{
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
id responseObject = self.responseObject;
if (self.error) {
if (failure) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
success(self, responseObject);
});
}
}
}
if (self.completionGroup) {
dispatch_group_leave(self.completionGroup);
}
});
};
#pragma clang diagnostic pop
}
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
添加完成块。生成完成后加入到自己的operationQueue中在合适的时机分配线程给AFHTTPRequestOperation,调用AFHTTPRequestOperation的start方法
- (void)start {
[self.lock lock];
if ([self isCancelled]) {
[self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
} else if ([self isReady]) {
self.state = AFOperationExecutingState;
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
}
[self.lock unlock];
}
start中调用了下面这个函数
[self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]];
这个是中有一个
[[self class] networkRequestThread]
这个是干什么的
+ (NSThread *)networkRequestThread {
static NSThread *_networkRequestThread = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[_networkRequestThread start];
});
return _networkRequestThread;
}
- (void)operationDidStart {
[self.lock lock];
if (![self isCancelled]) {
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[self.outputStream scheduleInRunLoop:runLoop forMode:runLoopMode];
}
[self.connection start];
}
[self.lock unlock];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self];
});
}
思考一下,从并发性的角度来看,AF并没有做什么,根本的并发还是系统底层对NSURLConnection的并发控制, 所谓operationQueue的并发是干什么用的,那要是我仅仅用一个线程来收发,并且没个请求都有相应的存储数据的数据结构和完成块就ok了,干嘛要用queue和NSOperation,既然AF用了就有用,因为他们是因为NSOperation可以cancel,可以暂停,等等,而且这些控制都由queue去管理,这样我们自己写的管理代码就少的多,而且我们管理线程肯定不能优于系统,所以,AF这种设计是非常合理的。