最近看AFNetworking2的源码,学习这个知名网络框架的实现,顺便梳理写下文章。AFNetworking2的大体架构和思路在这篇文章已经说得挺清楚了,就不再赘述了,只说说实现的细节。AFNetworking的代码还在不断更新中,我看的是AFNetworking2.3.1。
本篇先看看AFURLConnectionOperation,AFURLConnectionOperation继承自NSOperation,是一个封装好的任务单元,在这里构建了NSURLConnection,作为NSURLConnection的delegate处理请求回调,做好状态切换,线程管理,可以说是AFNetworking最核心的类,下面分几部分说下看源码时注意的点,最后放上代码的注释。
0.Tricks
AFNetworking代码中有一些常用技巧,先说明一下。
A.clang warning
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//code
#pragma clang diagnostic pop
表示在这个区间里忽略一些特定的clang的编译警告,因为AFNetworking作为一个库被其他项目引用,所以不能全局忽略clang的一些警告,只能在有需要的时候局部这样做,作者喜欢用?:符号,所以经常见忽略-Wgnu警告的写法,详见这里。
B.dispatch_once
为保证线程安全,所有单例都用dispatch_once生成,保证只执行一次,这也是iOS开发常用的技巧。例如:
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, ^{
af_url_request_operation_completion_queue = dispatch_queue_create("com.alamofire.networking.operation.queue", DISPATCH_QUEUE_CONCURRENT );
});
return af_url_request_operation_completion_queue;
}
C.weak & strong self
常看到一个block要使用self,会处理成在外部声明一个weak变量指向self,在block里又声明一个strong变量指向weakSelf:
__weak __typeof(self)weakSelf = self;
self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
}];
weakSelf是为了block不持有self,避免循环引用,而再声明一个strongSelf是因为一旦进入block执行,就不允许self在这个执行过程中释放。block执行完后这个strongSelf会自动释放,没有循环引用问题。
1.线程
先来看看NSURLConnection发送请求时的线程情况,NSURLConnection是被设计成异步发送的,调用了start方法后,NSURLConnection会新建一些线程用底层的CFSocket去发送和接收请求,在发送和接收的一些事件发生后通知原来线程的Runloop去回调事件。
NSURLConnection的同步方法sendSynchronousRequest方法也是基于异步的,同样要在其他线程去处理请求的发送和接收,只是同步方法会手动block住线程,发送状态的通知也不是通过RunLoop进行。
使用NSURLConnection有几种选择:
A.在主线程调异步接口
若直接在主线程调用异步接口,会有个Runloop相关的问题:
当在主线程调用[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]时,请求发出,侦听任务会加入到主线程的Runloop下,RunloopMode会默认为NSDefaultRunLoopMode。这表明只有当前线程的Runloop处于NSDefaultRunLoopMode时,这个任务才会被执行。但当用户滚动tableview或scrollview时,主线程的Runloop是处于NSEventTrackingRunLoopMode模式下的,不会执行NSDefaultRunLoopMode的任务,所以会出现一个问题,请求发出后,如果用户一直在操作UI上下滑动屏幕,那在滑动结束前是不会执行回调函