网络,常常和多线程搞混,以前想复杂了,其实采用最简洁的使用方式才是推荐的方式。
历史
大约在2012年左右的样子,关于iOS的网络第三方库,一般有三种观点
当时的普遍说法是ASI
已经停止更新;AF
比较小,使用比较简单;MK
有ASI
的效率,接口和AF
一样好用。
当时我所在的项目组选用的是MK
,当然现在接触的项目,网络库基本上选择AFNetworking
。这个基本上已经成为事实上的标准了。
另外一个比较相关的内容是图片的上传和下载。AFNetworking
其实是有图片上传和下载相关的功能的,但是实际接触的项目中用的非常少。SDWebImage基本上成为图片上传下载的事实标准。
学习资料
iOS
关于网络的API
,一开始是NSURLConnection
,当前,基本上已经过渡到NSURLSession
,下面几篇文章都是不错的学习资料
iOS开发网络篇—发送GET和POST请求(使用NSURLSession)
iOS开发 GET、POST请求方法:NSURLSession篇
数据业务
如果采用AFNetworking的话,按照网上的教程来,比如iOS开发——AFNetworking框架使用详解,相对比较省心。
如果想自己尝试写的话,那么就要多考虑一些了。
数据业务应该是APP
中使用最多的场景:
(1)从后台拿数据,显示页面;
(2)提交用户数据,提交后台处理,响应之后更新页面。
采用GET还是POST,或者两者都有?
- 从后台拿数据,显示页面
对于这种场景,GET
还是有优势的。不需要用户提供数据,仅仅是访问后台,像普通的浏览器访问网站一样,用GET
完全满足要求。
GET
访问,可以做缓存,加快访问速度。
GET
是NSURLSession
的默认方式,只要有URL
就可以,甚至连URLRequest
都不需要。
- (void)getWithUrl {
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//2.获得会话对象
NSURLSession *session = [NSURLSession sharedSession];
//3.根据会话对象创建一个Task(发送请求)
/*
第一个参数:请求路径
第二个参数:completionHandler回调(请求完成【成功|失败】的回调)
data:响应体信息(期望的数据)
response:响应头信息,主要是对服务器端的描述
error:错误信息,如果请求失败,则error有值
注意:
1)该方法内部会自动将请求路径包装成一个请求对象,该请求对象默认包含了请求头信息和请求方法(GET)
2)如果要发送的是POST请求,则不能使用该方法
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//5.解析数据
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
}];
//4.执行任务
[dataTask resume];
}
-
提交用户数据,提交后台处理,响应之后更新页面
这种场景,GET
就无能为力了,需要用到POST
方式;
仅仅URL
不够,需要用到URLRequest
;
用户数据,放在URLRequest
的HTTPBody
之中,NSString
转NSData
,使用NSUTF8StringEncoding
编码
URLRequest
的HTTPBody属性,需要设置为@"POST"
-
URLRequest
的头部,一般都需要加一些特殊信息,用来验证一个可信的链接。比如版本号,token,sign,deviceID
等等 -
POST
是无法避免的,而GET
方式可以采用和POST
一样的方式实现,也就是使用URLRequest
来实现。或者说,GET
方式能做的事可以用POST
方式来取代。
- (void)getWithURLRequest {
//对请求路径的说明
//http://120.25.226.186:32812/login?username=520it&pwd=520&type=JSON
//协议头+主机地址+接口名称+?+参数1&参数2&参数3
//协议头(http://)+主机地址(120.25.226.186:32812)+接口名称(login)+?+参数1(username=520it)&参数2(pwd=520)&参数3(type=JSON)
//GET请求,直接把请求参数跟在URL的后面以?隔开,多个参数之间以&符号拼接
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"];
//2.创建请求对象
//请求对象内部默认已经包含了请求头和请求方法(GET)
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.获得会话对象
NSURLSession *session = [NSURLSession sharedSession];
//4.根据会话对象创建一个Task(发送请求)
/*
第一个参数:请求对象
第二个参数:completionHandler回调(请求完成【成功|失败】的回调)
data:响应体信息(期望的数据)
response:响应头信息,主要是对服务器端的描述
error:错误信息,如果请求失败,则error有值
*/
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
//6.解析服务器返回的数据
//说明:(此处返回的数据是JSON格式的,因此使用NSJSONSerialization进行反序列化处理)
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(@"%@",dict);
}
}];
//5.执行任务
[dataTask resume];
}
数据业务统一为POST
是推荐做法。引入GET
,反而要区分两种方式,更麻烦。另外,POST
的安全性要比GET
高一点。
回调方式采用delegate还是block?NSURLSession采用系统的单例还是自定义的?
-
数据业务,大多数的场景是开始网络操作,然后转菊花,数据来了之后,更新页面。基本上是一个异步转同步的使用方式。 当然,如果不愿意等,可以直接页面返回,取消这个网络操作。
-
delegate
的方式可以提供更精确的控制,比如连接开始,连接成功,成功返回,等等,提供的系统函数还是比较多的。 -
block
的方式就比较直接了,开始请求,然后异步返回结果。如果成功,给相应数据;如果失败,给错误信息。 -
系统默认的超时时间是
60s
,如果要更改,那么就需要自定义NSURLSession
-
系统的单例
NSURLSession
,是默认模式;如果要配置为及时模式和后台模式,需要进行自定义。
结合需求场景,数据业务,采用系统默认的单例NSURLSession
就可以了。采用NSURLSessionDataTask
的block
回调方式。采用delegate
方式过于复杂,没有必要
// 1.创建一个网络路径
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://172.16.2.254/php/phonelogin?yourname=%@&yourpas=%@&btn=login",yourname,yourpass]];
// 2.创建一个网络请求
NSURLRequest *request =[NSURLRequest requestWithURL:url];
// 3.获得会话对象
NSURLSession *session = [NSURLSession sharedSession];
// 4.根据会话对象,创建一个Task任务:
NSURLSessionDataTask *sessionDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"从服务器获取到数据");
/*
对从服务器获取到的数据data进行相应的处理:
*/ NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingMutableLeaves) error:nil];
}];
// 5.最后一步,执行任务(resume也是继续执行):
[sessionDataTask resume];
下载业务
-
一般情况,下载业务涉及得很少,可以不提供
-
要用到下载,一般会认为时间比较长,文件比较大
-
下载一般是异步的,并且不需要转菊花,只要显示一个进度条就可以了
-
断点续传也是一个比较多的需求
-
切换到后台,是否可以继续下载,看具体需求场景。最好不要这样做。因为这块目前功能还比较弱,问题比较多,使用也比较麻烦
下载业务采用自定的NSURLSession
,不用系统的单例。采用delegate
方式,不用block
方式。模式仍然采用默认的,不考虑及时模式和背景模式。使用NSURLSessionDownloadTask
图片上传和下载
-
大图的上传和下载一般比较少,可以不做重点考虑
-
一般图片大小限制在
K
级别,上M
的就应该考虑切分和压缩。等待时间应当限制在10s
以内。 -
图片没下载下来之前,一般会提供一张默认图片,下载完成后替换
-
有些场景,图片下载过程可以提供一个转菊花,下载完成后显示
-
是否采用表单数据那个
API
,可以根据情况选择。普通的数据业务,外面会包一层JSON
。当然,图片数据,可以看做是一种NSData
的数据成员,也是完全可以当做一种普通的数据业务的。 -
目前见到的实现方式,采用表单数据的居多。不过个人偏向统一为普通的数据业务。同时也不赞成一次传好几张图片的方式,一张张传,多调用几次接口不就好了。这样可以统一接口。
-
当然,一般图片资源服务器和后台数据服务器的URL地址是不一样的。不过这不构成图片上传下载使用特殊接口的理由。
图片的上传下载,看做是普通数据业务,也采用NSURLSessionDataTask
。使用block
的方式,没有必要采用delegate
。不需要考虑NSURLSessionUploadTask
和NSURLSessionDownloadTask
上传业务
这个遇到的比较少,可以暂时不考虑。当然,想百度网盘那种应用,这个是重点了。一般情况,不需要考虑NSURLSessionUploadTask
这种场景。
小结
-
通过POST方式,用好
NSURLSessionDataTask
,解决普通数据业务就可以了。 -
只是图片的上传下载,看做是普通的
NSURLSessionDataTask
就可以了。 -
NSURLSessionUploadTask
和NSURLSessionDownloadTask
基本不用考虑。 -
一般用系统提供的单例
NSURLSession
就可以了,没有必要自定义。用block
的回调方式就可以了,这个方便,没有必要用delegate
-
用AFNetworking和SDWebImage这两个第三方库当然支持,省心。
-
NSURLSession
本来就是多线程,异步的,再工作者线程中完成耗时工作,不需要再考虑NSOperation
和GCD
等多线程技术。采用最简洁的使用方式才是可取的。 -
至于任务管理,直接使用
NSURLSessionDataTask
就可以了,本身就有id
属性,不需要额外管理了。
网友评论