标签:
目前iOS开发中使用最多的网络访问框架就是AFNetworking了。作为一个第三方框架,用起来确实比直接使用iOS自带的要方便得多。
AFNetworking在github上可以直接下载。地址为:https://github.com/AFNetworking/AFNetworking 。
首先先看AFURLConnectionOperation类,继承自NSOperation。
@interface AFURLConnectionOperation : NSOperation
在这里构建了NSURLConnection,作为NSURLConnection的delegate处理请求回调,做好状态切换,线程管理,可以说是AFNetworking最核心的类。
1.1这里用枚举定义了网络访问状态。
typedef NS_ENUM(NSInteger, AFOperationState) { AFOperationPausedState = -1, AFOperationReadyState = 1, AFOperationExecutingState = 2, AFOperationFinishedState = 3, };
1.2为了保证线程安全,所有的单例都使用了dispatch_once,保证了其只执行一次,这种写法在iOS代码中随处可见,很普及。
static dispatch_group_t url_request_operation_completion_group() { static dispatch_group_t af_url_request_operation_completion_group; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_url_request_operation_completion_group = dispatch_group_create(); }); return af_url_request_operation_completion_group; } static dispatch_queue_t url_request_operation_completion_queue() { static dispatch_queue_t af_url_request_operation_completion_queue; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //这里的DISPATCH_QUEUE_CONCURRENT生成一个并发执行队列 af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT ); }); return af_url_request_operation_completion_queue; }
1.3大量使用了: typedef 原变量类型 别名
举个例子:typedef char(*pFun)(int) 使用时候:(*pFun)(2); 是不是很容易看懂?
作者是这样子写的:
typedef void (^AFURLConnectionOperationProgressBlock)(NSUInteger bytes, long long totalBytes, long long totalBytesExpected); typedef void (^AFURLConnectionOperationAuthenticationChallengeBlock)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge); typedef NSCachedURLResponse * (^AFURLConnectionOperationCacheResponseBlock)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse); typedef NSURLRequest * (^AFURLConnectionOperationRedirectResponseBlock)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse);
你会看到在变量声明的时候,声明的block真心看起来很清爽,是不是?
@property (readwrite, nonatomic, copy) AFURLConnectionOperationCacheResponseBlock cacheResponse; @property (readwrite, nonatomic, copy) AFURLConnectionOperationRedirectResponseBlock redirectResponse;
1.4在代码块中避免循环应用:
- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler { [self.lock lock]; if (!self.backgroundTaskIdentifier) { UIApplication *application = [UIApplication sharedApplication]; __weak __typeof(self)weakSelf = self; self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ __strong __typeof(weakSelf)strongSelf = weakSelf; if (handler) { handler(); } if (strongSelf) { [strongSelf cancel]; [application endBackgroundTask:strongSelf.backgroundTaskIdentifier]; strongSelf.backgroundTaskIdentifier = UIBackgroundTaskInvalid; } }]; } [self.lock unlock]; }
weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是因为一旦进入block执行,就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。
2.1、先看看网络请求发起方法,是调用了- (void)start开始的,接下来初始化connection链接。
self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO];
初始化完成之后,,调用网络请求start方法,解开线程锁,然后回到主线程中发送一个通知给通知中心:
dispatch_async(dispatch_get_main_queue(), ^{ [[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingOperationDidStartNotification object:self]; });
看到了吗?作者发送通知的时候,使用的关键字,不是乱写的,很规范!
NSString * const AFNetworkingOperationDidStartNotification = @"com.alamofire.networking.operation.start"; NSString * const AFNetworkingOperationDidFinishNotification = @"com.alamofire.networking.operation.finish";
2.2 关于代码中Runloop的操作,网上有一个解析,说得很清楚:
若直接在主线程调用异步接口,会有个Runloop相关的问题:
当在主线程调用 [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES] 时,请求发出,侦听任务会加入到主线程的 Runloop 下,RunloopMode 会默认为 NSDefaultRunLoopMode。这表明只有当前线程的Runloop 处于 NSDefaultRunLoopMode 时,这个任务才会被执行。但当用户滚动 tableview 或 scrollview 时,主线程的 Runloop 是处于 NSEventTrackingRunLoopMode 模式下的,不会执行 NSDefaultRunLoopMode 的任务,所以会出现一个问题,请求发出后,如果用户一直在操作UI上下滑动屏幕,那在滑动结束前是不会执行回调函数的,只有在滑动结束,RunloopMode 切回 NSDefaultRunLoopMode,才会执行回调函数。苹果一直把动画效果性能放在第一位,估计这也是苹果提升UI动画性能的手段之一。
所以若要在主线程使用 NSURLConnection 异步接口,需要手动把 RunloopMode 设为 NSRunLoopCommonModes。这个 mode 意思是无论当前 Runloop 处于什么状态,都执行这个任务。
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; [connection start];
AFNetworking 用的是在子线程中回调异步接口,创建了一条常驻线程专门处理所有请求的回调事件,这个模型跟 nodejs 有点类似。网络请求回调处理完,组装好数据后再给上层调用者回调,这时候回调是抛回主线程的,因为主线程是最安全的,使用者可能会在回调中更新UI,在子线程更新UI会导致各种问题,一般使用者也可以不需要关心线程问题。
2.3 AFURLConnectionOperation 有一把递归锁,在所有会访问/修改成员变量的对外接口都加了锁,因为这些对外的接口用户是可以在任意线程调用的,对于访问和修改成员变量的接口,必须用锁保证线程安全。
后面这一部分是转载的。
标签:
原文地址:http://my.oschina.net/caijunrong/blog/494099