iOS网络NSURLConnection使用详解
一、整体介绍
NSURLConnection是苹果提供的原生网络访问类,但是苹果很快会将其废弃,且由NSURLSession(iOS7以后)来替代。目前使用最广泛的第三方网络框架AFNetworking最新版本已弃用了NSURLConnection,那我们学习它还有什么用呢?
- 首先,苹果弃用它还是需要时间的,最起码到iOS10之后;
- 现在还有一些老项目会使用NSURLConnection,特别是2013年之前的项目,用户量基础还是很大的;
- 另外,不得不承认,有些公司还在用类似ASI这些经典的网络框架,所以还是很有必要学习NSURLConnection的。
二、使用的一般步骤
1 NSURL:请求地址,定义一个网络资源路径:
NSURL *url = [NSURL URLWithString:@"协议://主机地址/路径?参数&参数"];
解释如下:
- 协议:不同的协议,代表着不同的资源查找方式、资源传输方式,比如常用的http,ftp等
- 主机地址:存放资源的主机的IP地址(域名)
- 路径:资源在主机中的具体位置
- 参数:参数可有可无,也可以多个。如果带参数的话,用“?”号后面接参数,多个参数的话之间用&隔开
2 NSURLRequest:请求,根据前面的NSURL建立一个请求:
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15.0];
参数解释如下:
- url:资源路径
- cachePolicy:缓存策略(无论使用哪种缓存策略,都会在本地缓存数据),类型为美剧类型,取值如下:
- NSURLRequestUseProtocolCachePolicy = 0 //默认的缓存策略,使用协议的缓存策略
- NSURLRequestReloadIgnoringLocalCacheData = 1 //每次都从网络加载
- NSURLRequestReturnCacheDataElseLoad = 2 //返回缓存否则加载,很少使用
- NSURLRequestReturnCacheDataDontLoad = 3 //只返回缓存,没有也不加载,很少使用
- timeoutInterval:超时时长,默认60s
另外,还可以设置其它一些信息,比如请求头,请求体等等,如下:
注意,下面的request应为NSMutableURLRequest,即可变类型
// 告诉服务器数据为json类型
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
// 设置请求体(json类型)
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{@"userid":@"123456"} options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
3 发送请求:
NSURLConnection默认的请求类型为GET,下面分异步和同步两种介绍
异步请求
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
// 有的时候,服务器访问正常,但是会没有数据!
// 以下的 if 是比较标准的错误 处理代码!
if (connectionError != nil || data == nil) {
//给用户的提示信息
NSLog(@"网络不给力");
return;
}
}];
参数说明如下:
- completionHandler:请求响应后(或者请求超时)执行的代码,queue为代码添加到的队列,即block执行的线程
- NSURLResponse 为服务器的响应,真实类型为NSHTTPURLResponse,通常只在“下载”功能时,才会使用;下面是协议头的参数:
- URL:响应的URL,有的时候,访问一个URL地址,服务器可能会出现重定向,会定位到新的地址!
- MIMEType(Content-Type):服务器告诉客户端,可以用什么软件打开二进制数据!网络之所以丰富多采,是因为有丰富的客户端软件!栗子:windows上提示安装 Flash 插件
- expectedContentLength:预期的内容长度,要下载的文件长度,下载文件时非常有用
- suggestedFilename:"建议"的文件名,方便用户直接保存,很多时候,用户并不关心要保存成什么名字!
- textEncodingName:文本的编码名称 @"UTF8",大多数都是 UTF8
- statusCode:状态码,在做下载操作的时候,需要判断一下
- allHeaderFields:所有的响应头字典时候,用户并不关心要保存成什么名字!
- NSData 服务器返回的数据,例如json、xml(现在用的少)
- NSError 网络访问错误码
- NSURLResponse 为服务器的响应,真实类型为NSHTTPURLResponse,通常只在“下载”功能时,才会使用;下面是协议头的参数:
注意,block的执行线程为queue,如果block涉及到UI操作,则必须回到主线程:[NSOperationQueue mainQueue]
发送同步请求
// 同步请求,代码会阻塞在这里一直等待服务器返回,如果data为nil则请求失败,当获取少量数据时可以使用此方法。
// request参数同上。
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
三、举例说明
分三个部分:
- 网络请求(json、xml数据)
- 文件下载
- 文件上传,这里例子不再给出,请参考这里:http://www.cnblogs.com/mddblog/p/5215453.html
1 一般的网络请求
/// 两种请求方式
typedef enum {
MethodGET,
MethodPOST
}Method;
/// 请求成功后,直接将jsonData转为jsonDict
+ (void)connectionRequestWithMethod:(Method)method URLString:(NSString *)URLString parameters:(NSDictionary *)dict success:(void (^)(id JSON))success fail:(void (^)(NSError *error))fail {
// 简单的转码,如果参数带有?&特殊字符,下面方法不适合
URLString = [URLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// 1.创建请求
NSURL *url = [NSURL URLWithString:URLString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 请求方式,默认为GET
if (method == MethodPOST) {
request.HTTPMethod = @"POST";
}
// 根据需要设置
[request setValue:@"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" forHTTPHeaderField:@"Accept"];
// 2.设置请求头 Content-Type 返回格式
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
// 3.设置请求体 NSDictionary --> NSData
if (dict != nil) {
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = data;
}
// 4.发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if ((data != nil) && (connectionError == nil)) {
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
if (success) {
success(jsonDict);
}
} else {
if (fail) {
fail(connectionError);
}
}
}];
}
2 文件下载
下面举一个实现断点续传的网络下载,要实现断点续传,还需要用到NSURLConnectionDataDelegate代理,NSFileHandle文件操作或者数据流的形式(NSOutputStream),这里以NSFileHandle为例,访问的服务器采用Apache搭建的本地服务器,具体见代码:
@interface ViewController () <NSURLConnectionDataDelegate>
/// 文件下载完毕之后,在本地保存的路径
@property (nonatomic, copy) NSString *filePath;
/// 需要下载的文件的总大小!
@property (nonatomic, assign) long long serverFileLength;
/// 当前已经下载长度
@property (nonatomic, assign) long long localFileLength;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
/// 点击屏幕事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self downloadFile];
}
/// 下载文件
- (void)downloadFile {
// 文件存储路径
self.filePath = @"/Users/username/Desktop//陶喆 - 爱很简单.mp3";
// 要下载的网络文件,采用Apache搭建的本地服务器
NSString *urlStr = @"http://localhost/陶喆 - 爱很简单.mp3";
// 简单的转码,如果参数带有?&特殊字符,下面方法不适合
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置请求方法为 HEAD 方法,这里只是头,数据量少,用同步请求也可以,不过最好还是用异步请求
request.HTTPMethod = @"HEAD";
// 无论是否会引起循环引用,block里面都使用弱引用
__weak typeof(self) weakSelf = self;
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
// 出错则返回
if (connectionError != nil) {
NSLog(@"connectionError = %@",connectionError);
return ;
}
// 记录需要下载的文件总长度
long long serverFileLength = response.expectedContentLength;
weakSelf.serverFileLength = serverFileLength;
// 初始化已下载文件大小
weakSelf.localFileLength = 0;
NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:weakSelf.filePath error:NULL];
long long localFileLength = [dict[NSFileSize] longLongValue];
// 如果没有本地文件,直接下载!
if (!localFileLength) {
// 下载新文件
[weakSelf getFileWithUrlString:urlStr];
}
// 如果已下载的大小,大于服务器文件大小,肯定出错了,删除文件并从新下载
if (localFileLength > serverFileLength) {
// 删除文件 remove
[[NSFileManager defaultManager] removeItemAtPath:self.filePath error:NULL];
// 下载新文件
[self getFileWithUrlString:urlStr];
} else if (localFileLength == serverFileLength) {
NSLog(@"文件已经下载完毕");
} else if (localFileLength && localFileLength < serverFileLength) {
// 文件下载了一半,则使用断点续传
self.localFileLength = localFileLength;
[self getFileWithUrlString:urlStr WithStartSize:localFileLength endSize:serverFileLength-1];
}
}];
}
/// 重新开始下载文件(代理方法监听下载过程)
-(void)getFileWithUrlString:(NSString *)urlString
{
NSURL *url = [NSURL URLWithString:urlString];
// 默认就是 GET 请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 开始请求,并设置代理为self
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
// 设置代理的执行线程,一般不在主线程
[conn setDelegateQueue:[[NSOperationQueue alloc] init]];
// NSUrlConnection 的代理方法是一个特殊的事件源!
// 开启运行循环!
CFRunLoopRun();
}
/* 断点续传(代理方法监听下载过程)
* startSize:本次断点续传开始的位置
* endSize:本地断点续传结束的位置
*/
-(void)getFileWithUrlString:(NSString *)urlString WithStartSize:(long long)startSize endSize:(long long)endSize
{
// 1. 创建请求!
NSURL *url = [NSURL URLWithString:urlString];
// 默认就是 GET 请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置断点续传信息
NSString *range = [NSString stringWithFormat:@"Bytes=%lld-%lld",startSize,endSize];
[request setValue:range forHTTPHeaderField:@"Range"];
// NSUrlConnection 下载过程!
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
// 设置代理的执行线程// 传一个非主队列!
[conn setDelegateQueue:[[NSOperationQueue alloc] init]];
// NSUrlConnection 的代理方法是一个特殊的事件源!
// 开启运行循环!
CFRunLoopRun();
}
#pragma mark - NSURLConnectionDataDelegate
/// 1. 接收到服务器响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// 服务器响应
// response.expectedContentLength的大小是要下载文件的大小,而不是文件的总大小。比如请求头设置了Range[requestM setValue:rangeStr forHTTPHeaderField:@"Range"];则返回的大小为range范围内的大小
}
/// 2. 接收到数据
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//data当前接收到的网络数据;
// 如果这个文件不存在,响应的文件句柄就不会创建!
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.filePath];
// 在这个路径下已经有文件了!
if (fileHandle) {
// 将文件句柄移动到文件的末尾
[fileHandle seekToEndOfFile];
// 写入文件的意思(会将data写入到文件句柄所操纵的文件!)
[fileHandle writeData:data];
[fileHandle closeFile];
} else {
// 第一次调用这个方法的时候,在本地还没有文件路径(没有这个文件)!
[data writeToFile:self.filePath atomically:YES];
}
}
/// 3. 接收完成
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(@"下载完成");
}
/// 4. 网络错误
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"下载错误 %@", error);
}
@end
四、NSData 数据的反序列化
服务器返回的NSData数据类型,事先已经知道,因此可以直接返序列化为实际的类型,例如:json(字典或数组)、.plist(字典或数组)、text、xml等:
- 加载数据
// 加载本地
NSString *path = [[NSBundle mainBundle] pathForResource:@"文件名 " ofType:nil];
NSData *jsonData = [NSData dataWithContentsOfFile:path];
// 从网络直接加载
NSURL *url = [[NSBundle mainBundle] URLForResource:@"topic_news.json" withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
- .plist反序列化
这种很少使用,只是苹果自己的格式
// 关于选型参数
// NSPropertyListImmutable = 0,不可变
// NSPropertyListMutableContainers = 1 << 0,容器可变
// NSPropertyListMutableContainersAndLeaves = 1 << 1,容器和叶子可变
// 通常后续直接做字典转模型,不需要关心是否可变,所以一般直接赋值为0
id result = [NSPropertyListSerialization propertyListWithData:data options:0 format:NULL error:NULL];
- 关于json数据
- json数据的本质是字符串,根格式只有两种可能:数组或字典。
- 反序列化:从服务器接收到的二进制数据 转换成 字典或者数组
// 假设json数据位:[{键值对},{键值对}],则可以将数据转化为字典或数组
NSArray *dictArray = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];- 序列化:将字典或者数组 转换成 二进制数据,准备发送给服务器
// obj为字典或数组
NSData *data = [NSJSONSerialization dataWithJSONObject:obj options:0 error:NULL];
// 转化前可以判断是否可以序列化:
+ (BOOL)isValidJSONObject:(id)obj; - 一个对象能够被转换成 JSON 必须符合以下条件:
- 顶级节点,必须是一个 NSArray or NSDictionary
- 所有的对象必须是 NSString, NSNumber, NSArray, NSDictionary, or NSNull
- 所有字典的 key 都必须是 NSString
- NSNumber 不能为空或者无穷大
- json数据的本质是字符串,根格式只有两种可能:数组或字典。
注意事项:
- 请求的缓存策略使用 NSURLRequestReloadIgnoringCacheData,忽略本地缓存
- 服务器响应结束后,要记录 Etag,服务器内容和本地缓存对比是否变化的重要依据!
- 在发送请求时,设置 If-None-Match,并且传入 etag
- 连接结束后,要判断响应头的状态码,如果是 304,说明本地缓存内容没有发生变化,此时可以使用本地缓存来加载数据,如果缓存文件被意外删除,程序依然运行但会报错,加载数据也失败:NSCachedURLResponse *cachedURLRes = [[NSURLCache sharedURLCache] cachedResponseForRequest:requestM];//cachedURLRes为nil
- GET 缓存的数据会保存在 Cache 目录中 /bundleId 下,Cache.db 中
iOS9访问http网页
- 在Info.plist中添加NSAppTransportSecurity类型Dictionary;
- 在NSAppTransportSecurity下添加NSAllowsArbitraryLoads类型Boolean,值设为YES
iOS网络NSURLConnection使用详解的更多相关文章
- iOS网络NSURLSession使用详解
一.整体介绍 NSURLSession在2013年随着iOS7的发布一起面世,苹果对它的定位是作为NSURLConnection的替代者,然后逐步将NSURLConnection退出历史舞台.现在使用 ...
- IOS 友盟使用详解
IOS 友盟使用详解 这篇博客将会详细介绍友盟的使用,希望对博友们有所帮助. 首先我们在浏览器上搜索友盟. 在这里我们选择官网这个,进去友盟官网后我们按照下图进行选择. 接下来选择如下图 Next 这 ...
- ios新特征 ARC详解
IOS ARC 分类: IOS ARC2013-01-17 09:16 2069人阅读 评论(0) 收藏 举报 目录(?)[+] 关闭工程的ARC(Automatic Reference Co ...
- iOS 单元测试之XCTest详解(一)
iOS 单元测试之XCTest详解(一) http://blog.csdn.net/hello_hwc/article/details/46671053 原创blog,转载请注明出处 blog.csd ...
- iOS学习之UINavigationController详解与使用(一)添加UIBarButtonItem
http://blog.csdn.net/totogo2010/article/details/7681879 1.UINavigationController导航控制器如何使用 UINavigati ...
- IOS—UITextFiled控件详解
IOS—UITextFiled控件详解 //初始化textfield并设置位置及大小 UITextField *text = [[UITextField alloc]initWithFrame:CGR ...
- [转]iOS学习之UINavigationController详解与使用(三)ToolBar
转载地址:http://blog.csdn.net/totogo2010/article/details/7682641 iOS学习之UINavigationController详解与使用(二)页面切 ...
- Centos7 配置网络步奏详解
Centos7 配置网络步奏详解 编辑网卡配置文件 vi /etc/sysconfig/network-script/ifcfg-ens01 备注:这里的ens01不是所有系统都叫这个,有的可能叫其他 ...
- iOS原生地图开发详解
在上一篇博客中:http://my.oschina.net/u/2340880/blog/414760.对iOS中的定位服务进行了详细的介绍与参数说明,在开发中,地位服务往往与地图框架结合使用,这篇博 ...
随机推荐
- laravel5.4中{{$name}} 和 {{!! $name !!}} 的区别:后者原生输出。前者转义
- TP3.2设置URL伪静态满足更好的SEO效果
URL伪静态通常是为了满足更好的SEO效果,ThinkPHP支持伪静态URL设置,可以通过设置URL_HTML_SUFFIX参数随意在URL的最后增加你想要的静态后缀,而不会影响当前操作的正常执行. ...
- 深入PHP内核之array_multisort
这个函数是我第一次看手册的时候,没看明白是怎么回事,所以有必要记录一下 用法 bool array_multisort ( array &$arr [, mixed $arg = SORT_A ...
- 提高PHP编码的一些技巧
1.不要使用相对路径 例如 require_once('../../lib/some_class.php'); 该方法有很多缺点: 1)它首先查找指定的php包含路径, 然后查找当前目录 2)如果该脚 ...
- python网络编程详解
最近在看<UNIX网络编程 卷1>和<FREEBSD操作系统设计与实现>这两本书,我重点关注了TCP协议相关的内容,结合自己后台开发的经验,写下这篇文章,一方面是为了帮助有需要 ...
- php数据库操作类(转)
<?php Class DB { private $link_id; private $handle; private $is_log; private $t ...
- C#--类成员
数据成员: 字段: 常量: 函数成员(执行代码): 方法: 属性 构造函数 析构函数 运算符 索引 事件 可以被声明为Static的类成员是:
- oracle各种常用管理sql及其他 ---待续
启动客户端工具:sqlplus /nolog 使用sysdba链接:conn / as sysdba; select * from dba_users; --查看数据库里面所有用户,前提是你是有dba ...
- 后缀crt证书转换
转换地址:https://www.chinassl.net/ssltools/convert-ssl.html 转换成功后点下载即可
- introduction to python for statistics,analysis笔记2
一.行列式连接concatenate函数,axis=0是垂直拼接,axis=1是水平拼接 x=np.array([[],[,]]); y=np.array([[],[,]]); z=np.concat ...