iOS 中,关于网络请求的接口自下至上有以下几层:git
CFSocket CFNetwork ->ASIHttpRequest NSURLConnection ->AFNetworking NSURLSession ->AFNetworking2, Alamofire
• CFSocket 是最底层的接口,只负责 socket 通讯。
• CFNetwork 是基于 CFSocket 等接口的上层封装,ASIHttpRequest 工做于这一层。
• NSURLConnection 是基于 CFNetwork 的更高层的封装,提供面向对象的接口,AFNetworking 工做于这一层。
• NSURLSession 是 iOS7 中新增的接口,表面上是和 NSURLConnection 并列的,但底层仍然用到了 NSURLConnection 的部分功能 (好比 com.apple.NSURLConnectionLoader 线程),AFNetworking2 和 Alamofire 工做于这一层。github
下面主要介绍下 NSURLConnection 的工做过程。网络
一般使用 NSURLConnection 时,你会传入一个 Delegate,当调用了 [connection start] 后,这个 Delegate 就会不停收到事件回调。实际上,start 这个函数的内部会会获取 CurrentRunLoop,而后在其中的 DefaultMode 添加了4个 Source0 (即须要手动触发的Source)。CFMultiplexerSource 是负责各类 Delegate 回调的,CFHTTPCookieStorage 是处理各类 Cookie 的。app
当开始网络传输时,咱们能够看到 NSURLConnection 建立了两个新线程:com.apple.NSURLConnectionLoader 和 com.apple.CFSocket.private。其中 CFSocket 线程是处理底层 socket 链接的。NSURLConnectionLoader 这个线程内部会使用 RunLoop 来接收底层 socket 的事件,并经过以前添加的 Source0 通知到上层的 Delegate。框架
NSURLConnectionLoader 中的 RunLoop 经过一些基于 mach port 的 Source 接收来自底层 CFSocket 的通知。当收到通知后,其会在合适的时机向 CFMultiplexerSource 等 Source0 发送通知,同时唤醒 Delegate 线程的 RunLoop 来让其处理这些通知。CFMultiplexerSource 会在 Delegate 线程的 RunLoop 对 Delegate 执行实际的回调。socket
AFURLConnectionOperation 这个类是基于 NSURLConnection 构建的,其但愿能在后台线程接收 Delegate 回调。为此 AFNetworking 单首创建了一个线程,并在这个线程中启动了一个 RunLoop:函数
+ (void)networkRequestThreadEntryPoint:(id)__unused object { @autoreleasepool { [[NSThread currentThread] setName:@"AFNetworking"]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; } } + (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; }
RunLoop 启动前内部必需要有至少一个 Timer/Observer/Source,因此 AFNetworking 在 [runLoop run] 以前先建立了一个新的 NSMachPort 添加进去了。一般状况下,调用者须要持有这个 NSMachPort (mach_port) 并在外部线程经过这个 port 发送消息到 loop 内;但此处添加 port 只是为了让 RunLoop 不至于退出,并无用于实际的发送消息。oop
- (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]; }
当须要这个后台线程执行任务时,AFNetworking 经过调用 [NSObject performSelector:onThread:..] 将这个任务扔到了后台线程的 RunLoop 中。spa
AsyncDisplayKit 是 Facebook 推出的用于保持界面流畅性的框架,其原理大体以下:线程
UI 线程中一旦出现繁重的任务就会致使界面卡顿,这类任务一般分为3类:排版,绘制,UI对象操做。
排版一般包括计算视图大小、计算文本高度、从新计算子式图的排版等操做。
绘制通常有文本绘制 (例如 CoreText)、图片绘制 (例如预先解压)、元素绘制 (Quartz)等操做。
UI对象操做一般包括 UIView/CALayer 等 UI 对象的建立、设置属性和销毁。
其中前两类操做能够经过各类方法扔到后台线程执行,而最后一类操做只能在主线程完成,而且有时后面的操做须要依赖前面操做的结果 (例如TextView建立时可能须要提早计算出文本的大小)。ASDK 所作的,就是尽可能将能放入后台的任务放入后台,不能的则尽可能推迟 (例如视图的建立、属性的调整)。
为此,ASDK 建立了一个名为 ASDisplayNode 的对象,并在内部封装了 UIView/CALayer,它具备和 UIView/CALayer 类似的属性,例如 frame、backgroundColor等。全部这些属性均可以在后台线程更改,开发者能够只经过 Node 来操做其内部的 UIView/CALayer,这样就能够将排版和绘制放入了后台线程。可是不管怎么操做,这些属性总须要在某个时刻同步到主线程的 UIView/CALayer 去。
ASDK 仿照 QuartzCore/UIKit 框架的模式,实现了一套相似的界面更新的机制:即在主线程的 RunLoop 中添加一个 Observer,监听了 kCFRunLoopBeforeWaiting 和 kCFRunLoopExit 事件,在收到回调时,遍历全部以前放入队列的待处理的任务,而后一一执行。
具体的代码能够看这里:_ASAsyncTransactionGroup。