一个异步网络请求的坑:关于NSURLConnection和NSRunLoopCommonModes
2015-11-09 13:53
459 查看
一个异步网络请求的坑:关于NSURLConnection和NSRunLoopCommonModes
我们开发App时,常常需要异步下载网络资源或者实现REST API调用,目前流行的HTTP库有ASIHTTPRequest(已经停止开发维护)和AFNetWorking。两者实现异步网络请求的方式不太相同,ASIHTTPRequest使用的是NSOperation+CFNetWork
API实现异步网络请求,但是在一个公共独立子线程上去执行网络请求:
1 2 3 4 5 6 7 8 9 10 11 12 | + (NSThread *)threadForRequest:(ASIHTTPRequest *)request { if (networkThread == nil) { @synchronized(self) { if (networkThread == nil) { networkThread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequests) object:nil]; [networkThread start]; } } } return networkThread; } |
1 2 3 4 5 6 7 8 9 10 | + (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; } |
首先,如果是直接调用NSURLConnection的initWithRequest:delegate:startImmediately:(第三个参数用YES,这个是designated initializer)或者方法initWithRequest:delegate:时,NSURLConnection会默认运行在NSDefaultRunLoopMode模式下,即使再使用scheduleInRunLoop:forMode:设置运行模式也没有用。如果NSURLConnection运行在NSDefaultRunLoopMode下,何为Run
Loop的模式Mode,请参考这篇Blog), 这篇Blog提到NSDefaultRunLoopMode是Run
Loop默认的运行模式,用于处理除了NSConnection对象的事件。 然而如果NSURLConnection是运行在NSDefaultRunLoopMode,而当前线程是主线程,并且UI上有类似滚动这样的操作,那么主线程的Run Loop会运行在UITrackingRunLoopMode下,就无法响应NSURLConnnection的回调。此时需要首先使用initWithRequest:delegate:startImmediately:(第三个参数为NO)生成NSURLConnection,再重新设置NSURLConnection的运行模式为NSRunLoopCommonModes,那么UI操作和回调的执行都将是非阻塞的,因为NSRunLoopCommonModes是一组run
loop mode的集合,默认情况下包含了NSDefaultRunLoopMode和UITrackingRunLoopMode。
1 2 3 4 5 6 7 8 9 10 11 1213 | - (void)start { NSMutableURLRequest* request = [[NSMutableURLRequest alloc] initWithURL:self.URL cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:self.timeoutInterval]; [request setHTTPMethod: @"GET"]; self.connection =[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; [self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; [self.connection start]; } |
1 2 3 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){ [downloader start]; }); |
1 2 3 | [downloader performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:YES]; |
当然在GCD的全局队列里启动NSURLConnection需要这样这样:
1 2 34 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(){ [downloader start]; NSLog(@"current worker thread: %@", [NSThread currentThread]); [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; NSLog(@"exit worker thread"); }); |
1 | 2013-06-26 19:35:13.309 NSURLConnectionExample[22646:50b] connectionDidFinishLoading in main thread?: 1 |
1 | 2013-06-26 19:38:18.937 NSURLConnectionExample[22670:3903] connectionDidFinishLoading in main thread?: 0 |
样例程序里也实现了一个基于子线程的下载器PTThreadDownloader,与ASIHTTPRequest和AFNetWorking中的方法类似,生成一个公共子线程来启动NSURLConnection。这里没有必要对每个网络请求都生成一个后台子线程去启动NSURLConnection(例如上面那种用GCD扔到global queue的方式最终也是每次从线程池找到一个线程来启动NSURLConnection),因为底层网络IO并不是在这个子线程里去执行的,子线程仅仅用于响应NSURLConnection回调。不过公共子线程的方法会导致有一个子线程一直运行在后台,等待用户用它来启动NSURLConnection。
Sample Code
本文例子放在Github上(工程NSURLConnectionExample),可以根据文中的几种情况测试initWithRequest:delegate:startImmediately:第三个参数的影响以及回调问题,例子UI中的按钮每点击一次会产生一个下载PTNormalDownloaler对象并开始执行,如果这个NSURLConnection是运行在NSDefaultRunLoopMode模式下,那么上下滚动UI中的TableView,是不会触发NSURLConnection回调的,只有UI操作结束后才会触发。
相关文章推荐
- nc.exe 使用方法
- http://my.oschina.net/u/1245614/blog/481573?p={{page}}
- 打杂程序员之公司网络配置
- Android学习之Android中Http通信:Http协议
- TCP——_SYN、ACK_、FIN、RST、PSH、URG_详解
- Heritrix3.3.0-环境搭建(maven项目)
- HTTP长连接与短连接
- EventLoop,TcpClient,TcpServer 中的生命周期
- Heritrix3.3.0源码阅读 crawler-beans.cxml中URI过滤规则的配置
- Https 客户端与服务器交互过程梳理(转)
- java分别通过httpclient和HttpURLConnection获取图片验证码内容
- 网络安全培训笔记 (高清大图)
- TCP/IP,http,socket,长连接,短连接——小结(转)
- MFC学习笔记之——>MFC下的TCP通信的建立
- MFC学习笔记之——>MFC下的TCP通信的建立
- 计算机网络基础试题
- netstat -s tcp指标详解
- (二)给Centos配置网络以及使用xshell远程连接Centos
- 深度学习算法原理——神经网络的基本原理
- HTTP Header