尽管NSURLConnection
已经不建议使用了,iOS 7
取而代之的是NSURLSession
,但是本文依然打算剖析一下,其中一些思想还是值得学习一下,如果您不用NSURLConnection
,并且也不打算尝试使用,可以跳过本文。
一、概念介绍
NSURLConnection
是2003年随着第一版Safari
的发布而发布的,它不单单是一个网络请求类,而是指代Foundation
框架的URL
系统中的一系列关联的组件:NSURLRequest
、NSURLResponse
、NSURLProtocol
、NSHTTPCookieStorage
、NSURLCredentialStorage
以及同名类NSURLConnection
。
1. 异步处理代理
其中,NSURLRequest
被传递给NSURLConnection
。被委托对象(遵守以前的非正式协议
和
)异步返回一个NSURLResponse
以及包含服务器返回信息的NSData
。
2. 请求策略
在一个请求被发送到服务器之前,系统会先查询共享的缓存信息,然后根据策略(policy
)以及可用性(availability
)的不同,一个已经被缓存的响应可能会被立即返回。如果没有缓存的响应可用,则这个请求将根据我们指定的策略来缓存它的响应以便将来的请求可以使用。
3. 认证策略
在把请求发送给服务器的过程中,服务器可能会发出鉴权查询(authentication challenge
),这可以由共享的 cookie
或机密存储(credential storage
)来自动响应,或者由被委托对象来响应。发送中的请求也可以被注册的NSURLProtocol
对象所拦截,以便在必要的时候无缝地改变其加载行为。
4. 下载过程
NSURLConnection下载文件时,先是将整个文件下载到内存,然后再写入到沙盒,如果文件比较大,就会出现内存暴涨的情况。
二、使用步骤
请求步骤:
- 设置请求路径
- 创建请求对象(默认是GET请求,且已经默认包含了请求头)
- 发送网络请求
- 接收到服务器的响应后,解析响应体
0.常用代理介绍
/*
1.当接收到服务器响应的时候调用,该方法只会调用一次
第一个参数connection:监听的是哪个NSURLConnection对象
第二个参数response:接收到的服务器返回的响应头信息
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
/*
2.当接收到数据的时候调用,该方法会被调用多次
第一个参数connection:监听的是哪个NSURLConnection对象
第二个参数data:本次接收到的服务端返回的二进制数据(可能是片段)
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveData:(nonnull NSData *)data
/*
3.当服务端返回的数据接收完毕之后会调用
通常在该方法中解析服务器返回的数据
*/
-(void)connectionDidFinishLoading:(nonnull NSURLConnection *)connection
/*4.当请求错误的时候调用(比如请求超时)
第一个参数connection:NSURLConnection对象
第二个参数:网络请求的错误信息,如果请求失败,则error有值
*/
- (void)connection:(nonnull NSURLConnection *)connection didFailWithError:(nonnull NSError *)error
1. 各种方法请求
- 方法一:利用
sendSynchronousRequest
发送GET
请求,该请求是同步的
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@""];
//2.创建一个请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.把请求发送给服务器
//sendSynchronousRequest 阻塞式的方法,会卡住线程
NSHTTPURLResponse *response = nil;
NSError *error = nil;
/*
第一个参数:请求对象
第二个参数:响应头信息,当该方法执行完毕之后,该参数被赋值
第三个参数:错误信息,如果请求失败,则error有值
*/
//该方法是阻塞式的,会卡住线程
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
// 对data进行处理
- 方法二:利用
sendAsynchronousRequest
发送POST
请求,该请求是异步的
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@""];
//2.创建一个请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 2.1设置请求方式
// 注意: POST一定要大写
request.HTTPMethod = @"POST";
// 2.2设置请求体
// 注意: 如果是给POST请求传递参数: 那么不需要写?号
request.HTTPBody = [@"username=Mitchell&pwd=123456&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
//3.把请求发送给服务器,发送一个异步请求
/*
第一个参数:请求对象
第二个参数:回调方法在哪个线程中执行,如果是主队列则block在主线程中执行,非主队列则在子线程中执行
第三个参数: completionHandlerBlock块:接受到响应的时候执行该block中的代码
response:响应头信息
data:响应体
connectionError:错误信息,如果请求失败,那么该参数有值
*/
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
//4.解析服务器返回的数据
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
//转换并打印响应头信息
NSHTTPURLResponse *r = (NSHTTPURLResponse *)response;
}];
- 方法三:通过设置代理,来处理请求响应或数据
// 第一种代理方式,自动发送请求
[[NSURLConnection alloc]initWithRequest:request delegate:self];
/*
第一个参数:请求对象
第二个参数:谁成为NSURLConnetion对象的代理
第三个参数:是否马上发送网络请求,如果该值为YES则立刻发送,如果为NO则不会发送网路请求
*/
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//在startImmediately为NO时,调用该方法控制网络请求的发送
[conn start];
// 第三种代理方式
//设置代理的第三种方式:使用类方法设置代理,会自动发送网络请求
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
//取消网络请求
//[conn cancel];
2. NSURLConnection 与 NSRunLoop 的关联使用
主要是区分 NSURLConnection 在主线程和子线程发送网络请求的区别
- 主线程
// 直接发送网络请求,发送是异步的,但是代理方法是在主线程中执行的
NSURL *url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//这里分两种方式发送请求
//2.1 直接发送网络请求是异步的,但是回调方法是在主线程中执行的
//[[NSURLConnection alloc]initWithRequest:request delegate:self];
// 如果按照如下设置,那么回调的代理方法也会运行在子线程中
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
NSURLRequest*request = [NSURLRequest requestWithURL:url];
//2.2 设置回调方法也在子线程中运行
NSURLConnection*conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
[conn setDelegateQueue:[[NSOperationQueue alloc] init]];
[conn start];
- 子线程
因为 NSURLConnection
是局部变量,当我们创建的时候其实是会默认添加到当前的 RunLoop
中,如果是在主线程添加,主线程的 RunLoop
是默认有的,无须我们创建;然而如果在子线程中,是默认没有 RunLoop
和输入源的,所以需要给子线程手动添加 RunLoop
。
调用start
方法时,如果没有 RunLoop
,会默认添加一个 RunLoop
到当前的线程中来,然后将connection
加到runLoop
中。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSRunLoop *loop = [NSRunLoop currentRunLoop];
[NSURLConnection connectionWithRequest:request delegate:self];
[loop run];
// 下面这样无法发送请求
[NSURLConnection connectionWithRequest:request delegate:self];
});
至此,NSURLConnection
的简介结束,只是提供了最简单应用的参考,如有不足之处,请不吝指教。下一篇简介目前推荐应用的NSURLSession
,敬请期待......
参考文档
http://www.jianshu.com/p/056b1817d25a
http://www.jianshu.com/p/982a3035c93a
http://www.jianshu.com/p/63e2ad28459f
http://www.jianshu.com/p/f291ee58c012