在2013年WWDC上苹果揭开了NSURLSession的面纱,将它作为NSURLConnection的继任者。现在使用最广泛的第三方网络框架:AFNetworking、SDWebImage等等都使用了NSURLSession。作为iOS开发人员,应该紧随苹果的步伐,不断的学习,无论是软件的更新、系统的更新、API的更新,而不能墨守成规。

  • 相比较NSURLConnection,NSURLSession提供了 配置会话缓存、协议、cookie和证书能力,这使得网络架构和应用程序可以独立工作、互不干扰。
  • 另外,NSURLSession另一个重要的部分是 会话任务,它负责加载数据,在客户端和服务器端进行文件的上传下载。

下面让我们正式进入NSURLSession学习。

一、NSURLSession介绍

在NSURLSession时代,网络请求基本上由3个任务完成:
  • NSURLSessionData:请求数据任务
  • NSURLSessionUploadTask:请求上传任务
  • NSURLSessionDownloadTask:请求下载任务
关系图如下:

NSURLSessionTask支持任务的暂停、取消和恢复,并且默认任务运行在其他非主线程中

二、NSURLSession使用

说了这么多,是时候来露两手了,具体NSURLSession怎么用呢?

1. 数据请求

先看一个网络数据请求实例,和上一章的NSURLConnection请求对比参考:
- (void)loadJsonData{
//1.创建url
NSString *urlStr = @"http://192.168.1.208/ViewStatus.aspx?userName=KenshinCui&password=123";
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
//2.创建请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.创建会话(这里使用了一个全局会话)
NSURLSession *session = [NSURLSession sharedSession];
//4.通过会话创建任务
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",dataStr);
}else{
NSLog(@"error is :%@",error.localizedDescription);
}
}];
//5.每一个任务默认都是挂起的,需要调用 resume 方法启动任务
[dataTask resume];
}

不难发现NSURLSession网络请求的五步走黄金油战略

  1. 创建NSURL
  2. 创建NSURLRequest
  3. 创建会话NSURLSession
  4. 通过会话创建任务NSURLSessionTask的子类
  5. 调用resume方法,启动任务

2. 文件下载

文件下载也是一样的,只是换上下载任务NSURLSessionDownloadTask就行,对回调做不同处理,一切都要贯彻五步走战略,O(∩_∩)O哈!

常用的创建文件下载任务的方法如下:
/* 回调类型,这是我为了排版方便抽出来的,实际框架中没有 */
typedef void (^downloadCompletionBlock)(NSURL*,NSURLReponse*,NSError*);
/* 创建文件下载任务,需要请求NSURLRequest */
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
completionHandler:(downloadCompletionBlock)completion;
/* 创建文件任务,简化了一些操作,只需要URL就能进行文件下载 */
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url
completionHandler:(downloadCompletionBlock)completion;
下面是下载实例
-(void)downloadFile{
//1.创建url
NSString *fileName = @"1.jpg";
NSString *urlStr = [NSString stringWithFormat: @"http://192.168.1.208/FileDownload.aspx?file=%@",fileName];
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
//2.创建请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//3.创建会话(这里使用了一个全局会话)
NSURLSession *session = [NSURLSession sharedSession];
//4.创建文件下载任务
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (!error) {
//注意location是下载后的临时保存路径,需要将它移动到需要保存的位置
NSError *saveError;
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *savePath = [cachePath stringByAppendingPathComponent:fileName];
NSURL *saveUrl = [NSURL fileURLWithPath:savePath];
[[NSFileManager defaultManager] copyItemAtURL:location toURL:saveUrl error:&saveError];
if (!saveError) {
NSLog(@"save sucess.");
}
}
}];
//5.启动任务
[downloadTask resume];
}
  • 回调中的location是下载后的临时保存路径,需要将它移动到需要保存的位置
  • NSFileManager的对象方法
    ```objc
    //将fromURL路径下的文件拷贝到toURL路径下
  • (void)copyItemAtURL:(NSURL )fromUrl
    toURL:(NSURL 
    )toUrl
    error:(NSError **)error;
    ```

3.文件上传

使用NSURLConnection的文件上传时,我们还需要自己构建上传请求,主要是拼接上传表单,这是个十分麻烦的过程。
现在使用NSURLSessionUploadTask文件上传任务,我们就可以解放了,简单粗暴。
\(^o^)/~

下面是常用的创建上传任务的方法:
/* 回调类型,这是我为了排版方便抽出来的,实际框架中没有 */
typedef void (^UploadCompletionBlock)(NSData*,NSURLReponse*,NSError*);
/* 创建上传任务,需要提供上传文件二进制数据 */
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(NSData *)bodyData
completionHandler:(UploadCompletionBlock)completion;
/* 创建上传任务,需要提供上传文件所在的URL路径,不过这个方法常配合“PUT”请求使用 */
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromFile:(NSURL *)fillURL
completionHandler:(UploadCompletionBlock)completion;
下面是上传实例:
- (void) NSURLSessionBinaryUploadTaskTest {
// 1.创建url,采用Apache本地服务器进行测试
NSString *urlStr = @"http://localhost/upload.php";
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
// 2.创建请求,这里要设置POST请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";// 文件上传使用post
// 3.获取全局会话Session
NSURLSession *session = [NSURLSession sharedSession];
// 4.创建上传任务,Request的Body Data将被忽略,而由fromData提供
NSData *data = [NSData dataWithContentsOfFile:@"/Users/userName/Desktop/IMG_0359.jpg"];
NSURLSessionUploadTask *upload =
[session uploadTaskWithRequest:request
fromData:data
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error == nil) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"upload success:%@",result);
} else {
NSLog(@"upload error:%@",error);
}
}]
// 5.启动任务
[upload resume];
}

是不是很简单,数据请求、文件下载、文件上传基本上都差不多,使用起来比NSURLConnection方便多了,还有什么理由不用NSURLSession呢!!

4.用dataTask上传文件【闲得蛋疼可以试一下】

除了上面的上传方式,实际上你也可以用NSURLSessionDataTask的方式上传,不过你就要自己设置上传BodyData和Header了,具体构建细节可以参考iOS学习笔记12-网络请求(一)NSURLConnection里面的构建过程,这里给个参考吧:

#pragma mark 上传文件
-(void)uploadFile{
NSString *fileName = @"pic.jpg";
//1.创建url
NSString *urlStr = @"http://192.168.1.208/FileUpload.aspx";
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
//2.创建请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
//3.构建上传表单数据
//设置数据体
NSData *data = [self getHttpBody:fileName];
request.HTTPBody = data;
//设置请求头
NSString *lengthStr = [NSString stringWithFormat:@"%lu",(unsigned long)data.length];
[request setValue:lengthStr forHTTPHeaderField:@"Content-Length"];
NSString *typeStr = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",kBOUNDARY_STRING];
[request setValue:typeStr forHTTPHeaderField:@"Content-Type"];
//4.创建会话
NSURLSession *session = [NSURLSession sharedSession];
//5.创建dataTask任务,去做上传的功能
NSURLSessionDataTask *uploadTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",dataStr);
}else{
NSLog(@"error is :%@",error.localizedDescription);
}
}];
//6.启动任务
[uploadTask resume];
}
上面的获取数据体方法getHttpBody,我也贴过来了
#pragma mark 取得数据体
-(NSData *)getHttpBody:(NSString *)fileName{
NSMutableData *dataM = [NSMutableData data];
NSString *type = [self getMIMETypes:fileName];
//构建请求体body的顶部
NSMutableString *bodyTop = [NSMutableString string];
//宏kBOUNDARY_STRING就是boundary标示
[bodyTop appendFormat:@"--%@\n",kBOUNDARY_STRING];
[bodyTop appendFormat:@"Content-Disposition: form-data; name=\"file1\"; filename=\"%@\"\n",fileName];
[bodyTop appendFormat:@"Content-Type: %@\n\n",type];
//构建请求体body的底部
NSString *bodyBottom = [NSString stringWithFormat:@"\n--%@--",kBOUNDARY_STRING];
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
//构建请求体body中间的二进制上传数据
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
//把顶部、数据、底部组合起来,形成body
[dataM appendData:[bodyTop dataUsingEncoding:NSUTF8StringEncoding]];
[dataM appendData:fileData];
[dataM appendData:[bodyBottom dataUsingEncoding:NSUTF8StringEncoding]];
return dataM;
}

三、会话Session控制

上面我们都是使用的全局NSURLSession,一般情况下我们就够用,但如果遇到两个连接使用不同的资源配置的情况,怎么办?答案就是自己定制。

  • NSURLSession支持我们自己定制NSURLSession
  • NSURLSession支持的三种会话配置:
  1. defaultSessionConfiguration
    进程内会话(默认会话),用硬盘来缓存数据。
  2. ephemeralSessionConfiguration
    临时的进程内会话(内存),不会将cookie、缓存储存到本地,只会放到内存中,当应用程序退出后数据也会消失。
  3. backgroundSessionConfiguration
    后台会话,相比默认会话,该会话会在后台开启一个线程进行网络数据处理。
下面就是定制NSURLSession的过程:
//使用默认会话配置
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = 5.0f;//请求超时时间
sessionConfig.allowsCellularAccess = true;//是否允许蜂窝网络下载(2G/3G/4G)
//创建会话,指定配置和代理
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
上面设置了代理,NSURLSession有很多代理协议:
  • NSURLSessionDelegateNSObject
    会话父协议
  • NSURLSessionTaskDelegateNSURLSessionDelegate
    任务协议
  • NSURLSessionDataDelegateNSURLSessionTaskDelegate
    数据协议
  • NSURLSessionDownloadDelegate: NSURLSessionTaskDelegate
    下载协议
  • NSURLSessionStreamDelegateNSURLSessionTaskDelegate
    网络流协议
下面就拿最常用的下载协议NSURLSessionDownloadDelegate来讲下:
/* 下载中(会多次调用,可以记录下载进度) */
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask /* 下载任务 */
didWriteData:(int64_t)bytesWritten /* 这次下载完成的字节数 */
totalBytesWritten:(int64_t)totalBytesWritten /* 已经下载完成的总字节数 */
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite; /* 需要下载完成的总字节数 */ /* 成功下载完成 */
-(void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask /* 下载任务 */
didFinishDownloadingToURL:(NSURL *)location;/* 下载完成后临时存放的URL */ /* 任务完成,不管是否下载成功 */
-(void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task /* 下载任务 */
didCompleteWithError:(NSError *)error;/* 错误 */
实际上NSURLSessionTask任务除了resume启动之外,还有一些方法
/* 取消任务 */
- (void)cancel;
/* 挂起任务(暂停任务) */
- (void)suspend;
/* 启动任务 */
- (void)resume;
下面来个代码总结:
-(void)downloadFile{
NSString *fileName = _textField.text;
NSString *urlStr = [NSString stringWithFormat: @"http://192.168.1.208/FileDownload.aspx?file=%@",fileName];
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStr];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = 5.0f;//请求超时时间
sessionConfig.allowsCellularAccess = true;//是否允许蜂窝网络下载(2G/3G/4G)
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
_downloadTask = [session downloadTaskWithRequest:request];
[_downloadTask resume];
}
#pragma mark 点击取消下载
-(void)cancelDownload{
[_downloadTask cancel];
}
#pragma mark 点击挂起下载
-(void)suspendDownload{
[_downloadTask suspend];
}
#pragma mark 点击恢复下载
-(void)resumeDownload{
[_downloadTask resume];
}
#pragma mark - 下载任务代理
#pragma mark 下载中(会多次调用,可以记录下载进度)
-(void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
[self setUIStatus:totalBytesWritten expectedToWrite:totalBytesExpectedToWrite];//设置界面状态
}
#pragma mark 下载完成
-(void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
NSError *error;
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *savePath = [cachePath stringByAppendingPathComponent:_textField.text];
NSURL *saveUrl = [NSURL fileURLWithPath:savePath];
[[NSFileManager defaultManager] copyItemAtURL:location toURL:saveUrl error:&error];
}
#pragma mark 任务完成,不管是否下载成功
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
[self setUIStatus:0 expectedToWrite:0];//设置界面状态
}

四、Session后台开启任务

NSURLSession支持程序的后台下载和上传,苹果官方将其称为进程之外的上传和下载,这些任务都是交给后台守护线程完成的,而非应用程序本身。
即使文件在下载和上传过程中崩溃了也可以继续运行(注意如果用户强制退关闭应用程序,NSURLSession会断开连接)。

我们先来看下如何创建一个后台Session
#pragma mark 取得一个后台会话(保证一个后台会话,这通常很有必要,这里采用单例模式的形式)
- (NSURLSession *)backgroundSession{
static NSURLSession *session = nil;
static dispatch_once_t token;//下面代码块只执行一次,以后都不执行
dispatch_once(&token, ^{
NSStirng *identifier = @"com.cmjstudio.URLSession";
NSURLSessionConfiguration *sessionConfig =
[NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
sessionConfig.timeoutIntervalForRequest = 5.0f;//请求超时时间
sessionConfig.discretionary = YES;//系统自动选择最佳网络下载
sessionConfig.HTTPMaximumConnectionsPerHost = 5;//限制每次最多5个连接
//创建会话
session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
});
return session;
}

然后我们拿到这个后台Session就可以做上面我们讲的下载和上传任务了。

我们来了解下程序进入后台后,任务是如何调度的,先上图:

当程序进入后台后,事实上任务是交给iOS系统来调度的,具体什么时候下载完成就不得而知,例如有个较大的文件经过一个小时下载完了,正常打开应用程序看到的此文件下载进度应该在100%的位置,但是由于程序已经在后台无法更新程序UI,而此时可以通过应用程序代理方法进行UI更新。

在AppDelegate.m中添加以下函数:
/*
有其中几个任务完成后,系统会调用此应用程序的该方法
此方法会包含一个competionHandler,通常我们会保存此对象
competionHandler此操作表示应用完成所有处理工作
*/
- (void)application:(UIApplication *)application
handleEventsForBackgroundURLSession:(NSString *)identifier
completionHandler:(void (^)())completionHandler
{
//backgroundSessionCompletionHandler是自定义的一个属性
self.backgroundSessionCompletionHandler = completionHandler;
}
在XXSession.m文件中实现NSURLSessionDelegate代理方法:
/*
直到最后一个任务完成,系统会调用该方法。
在这个方法中通常可以进行UI更新,并调用completionHandler通知系统已经完成所有操作。
*/
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; //这中间就可以写更新UI的代码了,code if (appDelegate.backgroundSessionCompletionHandler) {
void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
appDelegate.backgroundSessionCompletionHandler = nil;
completionHandler();
}
}

iOS学习笔记13-网络(二)NSURLSession的更多相关文章

  1. iOS学习笔记20-地图(二)MapKit框架

    一.地图开发介绍 从iOS6.0开始地图数据不再由谷歌驱动,而是改用自家地图,当然在国内它的数据是由高德地图提供的. 在iOS中进行地图开发主要有三种方式: 利用MapKit框架进行地图开发,利用这种 ...

  2. python 3.x 学习笔记13 (网络编程socket)

    1.协议http.smtp.dns.ftp.ssh.snmp.icmp.dhcp....等具体自查 2.OSI七层应用.表示.会话.传输.网络.数据链路.物理 3.socket: 对所有上层协议的封装 ...

  3. iOS学习笔记(十二)——iOS国际化

    开发的移动应用更希望获取更多用户,走向世界,这就需要应用国际化,国际化其实就是多语言.这篇文章介绍Xcode4.5以后的国际化,包括应用名国际化和应用内容国际化.如果是Xcode4.5之前版本请参考. ...

  4. iOS学习笔记-精华整理

    iOS学习笔记总结整理 一.内存管理情况 1- autorelease,当用户的代码在持续运行时,自动释放池是不会被销毁的,这段时间内用户可以安全地使用自动释放的对象.当用户的代码运行告一段 落,开始 ...

  5. IOS学习笔记06---C语言函数

    IOS学习笔记06---C语言函数 --------------------------------------------  qq交流群:创梦技术交流群:251572072              ...

  6. iOS学习笔记22-推送通知

    一.推送通知 推送通知就是向用户推送一条信息来通知用户某件事件,可以在应用退到后台后,或者关闭后,能够通过推送一条消息通知用户某件事情,比如版本更新等等. 推送通知的常用应用场景: 一些任务管理APP ...

  7. iOS学习笔记总结整理

    来源:http://mobile.51cto.com/iphone-386851_all.htm 学习IOS开发这对于一个初学者来说,是一件非常挠头的事情.其实学习IOS开发无外乎平时的积累与总结.下 ...

  8. IOS学习笔记48--一些常见的IOS知识点+面试题

      IOS学习笔记48--一些常见的IOS知识点+面试题   1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...

  9. React Native 学习笔记--进阶(二)--动画

    React Native 进阶(二)–动画 动画 流畅.有意义的动画对于移动应用用户体验来说是非常必要的.我们可以联合使用两个互补的系统:用于全局的布局动画LayoutAnimation,和用于创建更 ...

随机推荐

  1. webview.loadUrl()可能会URLDecoder

    与服务器交互时,客户端对userId进行了URLEncoder,然后在webview.loadUrl时将userId传给服务端,服务端再 URLDecoder userId 可是神奇的事情出现了,客户 ...

  2. hihoCoder #1079 : 离散化 (线段树,数据离散化)

    题意:有一块宣传栏,高一定,给出长度,再给出多张海报的张贴位置,问还能见到几张海报(哪怕有一点被看到)?假设海报的高于宣传栏同高. 思路:问题转成“给出x轴上长为L的一条线段,再用n条线段进行覆盖上去 ...

  3. 基于Vmware player的Windows 10 IoT core + RaspberryPi2安装部署

    本文记录了基于Vmware Player安装Windows10和VS2015开发平台的过程,以及如何在RaspberryPi2.0上启动Windows10 IoT core系统,并通过一个简单的hel ...

  4. Django添加tinyMCE编辑器

    tinymce的使用方法很简单,只需要在html页面中包含如下: <!-- Place inside the <head> of your HTML --> <scrip ...

  5. Hyperledger Fabric on SAP Cloud Platform

    今天的文章来自Wen Aviva, 坐Jerry面对面的程序媛. Jerry在之前的公众号文章<在SAP UI中使用纯JavaScript显示产品主数据的3D模型视图>已经介绍过Aviva ...

  6. Heacher互助平台 α版本冲刺

    课程属性 作业课程 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/ 作业链接 https://edu.cnblogs.co ...

  7. Java Web应用中获取用户请求相关信息,如:IP地址、操作系统、浏览器等信息

    引入jar包 <dependency> <groupId>eu.bitwalker</groupId> <artifactId>UserAgentUti ...

  8. QR 分解

    将学习到什么 介绍了平面旋转矩阵,Householder 矩阵和 QR 分解以入相关性质.   预备知识 平面旋转与 Householder 矩阵是特殊的酉矩阵,它们在建立某些基本的矩阵分解过程中起着 ...

  9. ant design table td 文字显示过长添加省略号、ant 文字过长时添加tootip提示

    方法1: overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit ...

  10. CS193p Lecture 4 - Foundation, Attributed Strings

    消息机制 调用一个实例(instance)的方法(method),就是向该实例的指针发送消息(message),实例收到消息后,从自身的实现(implementation)中寻找响应这条消息的方法. ...