初识NSURLSession

导语

因为苹果在IOS9 之后已经放弃了NSURLConnection,所以现在开发中,除了AFN框架,一般使用的就是iOS 107之后推出的NSURLSession(用于替代 NSURLConnection, 针对下载/上传等复杂的网络操作提供了专门的解决方案!)。所以今天我们聊聊NSURLSession。

核心类

1.NSURLSession:会话对象。
2.NSURLSessionConfiguration:创建会话对象的相关配置。
3.NSURLSessionTask:会话任务的基类。
4.NSURLSessionDataTask:网络通信,向服务器发送数据的同时,接受服务器反馈的信息。
5.NSURLSessionUploadTask:上传文件。
6.NSURLSessionDownloadTask:下载文件。

协议

NSURLSessionDelegate:会话的协议。
NSURLSessionTaskDelegate:任务的协议。
NSURLSessionDataDelegate:监听NSURLSessionDataTask和NSURLSessionUploadTask的工作状态。
NSURLSessionDownloadDelegate:监听NSURLSessionDownloadTask的工作状态。

NSURLSession的基本使用

//创建URL
    NSURL * url = [NSURL URLWithString:@"http://192.168.1.200/login.php?username=haha&password=123"];
    //创建请求
    //    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    //创建Session
    NSURLSession * session = [NSURLSession sharedSession];
    //创建任务(任务可以通过NSURL和NSURLRequest两种方式来初始化并通过block进行回调的方法)
    NSURLSessionDataTask * task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
//NSURLSessionDataTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       // NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);     
//    }];

    //开启网络任务
    [task resume];

NSURLConfiguration笔记

NSURLSessionConfiguration对象用于初始化NSURLSession对象。

  • (NSURLSessionConfiguration *)defaultSessionConfiguration;
    //返回标准配置,这实际上与NSURLConnection的网络协议栈是一样的,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。
  • (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
    //返回一个预设配置,没有持久性存储的缓存,Cookie或证书。这对于实现像"秘密浏览"功能的功能来说,是很理想的。
  • (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);
    //独特之处在于,它会创建一个后台会话。后台会话不同于常规的,普通的会话,它甚至可以在应用程序挂起,退出,
    崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文。
-(NSURLSession *)session
{
    if (_session == nil) {

        //创建NSURLSessionConfiguration
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];

        //设置请求超时为10秒钟
        config.timeoutIntervalForRequest = 10;

        //在蜂窝网络情况下是否继续请求(上传或下载)
        config.allowsCellularAccess = NO;

        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

NSURLSessionTask的各种子类使用

NSURLSessionTask 有两个子类

  • NSURLSessionDataTask,可以用来处理一般的网络请求,如 GET | POST 请求等
    • NSURLSessionDataTask 有一个子类为 NSURLSessionUploadTask,用于处理上传请求的时候有优势
  • NSURLSessionDownloadTask,主要用于处理下载请求,有很大的优势

NSURLSessionDataTask ———处理一般的网络请求

一:GET请求
//确定请求路径
 NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520&pwd=520&type=JSON"];
 //创建 NSURLSession 对象
 NSURLSession *session = [NSURLSession sharedSession];

 /**
  根据对象创建 Task 请求

  url  方法内部会自动将 URL 包装成一个请求对象(默认是 GET 请求)
  completionHandler  完成之后的回调(成功或失败)

  param data     返回的数据(响应体)
  param response 响应头
  param error    错误信息
  */
 NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:
             ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

     //解析服务器返回的数据
     NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
     //默认在子线程中解析数据
     NSLog(@"%@", [NSThread currentThread]);
 }];
 //发送请求(执行Task)
 [dataTask resume];

//发送GET请求的第二种方法
 /**
   注意:该block是在子线程中调用的,如果拿到数据之后要做一些UI刷新操作,那么需要回到主线程刷新
    第一个参数:需要发送的请求对象

  block:当请求结束拿到服务器响应的数据时调用block
    block-NSData:该请求的响应体
    block-NSURLResponse:存放本次请求的响应信息,响应头,真实类型为NSHTTPURLResponse
    block-NSErroe:请求错误信息
  */
//- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;

二:POST请求
//确定请求路径
 NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
 //创建可变请求对象
 NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];
 //修改请求方法
 requestM.HTTPMethod = @"POST";
 //设置请求体
 requestM.HTTPBody = [@"username=520&pwd=520&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
 //创建会话对象
 NSURLSession *session = [NSURLSession sharedSession];
 //创建请求 Task
 NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:requestM completionHandler:
             ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

     //解析返回的数据
     NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
 }];
 //发送请求
 [dataTask resume];
三:NSURLSessionDataTask 简单下载
//最简单的下载 
1. URL
    NSString *urlString = @"http://192.168.26.201/0805空缺数据 2.xls.zip";
    // 添加 % 转义
    urlString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:urlString];

    // NSURLSession,sharedSession是一个全局共享的单例,苹果为了方便程序员使用简单的网络任务
//    NSURLSession *session = [NSURLSession sharedSession];

    // 2. 由sharedSession发起网络会话任务
    [[[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {

        NSLog(@"%@", [NSBundle mainBundle].bundlePath);
        NSLog(@"%@", location);
    }] resume];
四:NSURLSessionDataDelegate代理方法
  • 使用NSURLSession发送GET请求(代理方式)
NSURLSession对象需要自定义
遵守协议NSURLSessionDataDelegate,实现相应的代理方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // 获得NSURLSession对象
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]];

    // 创建任务
    NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com/login?username=123&pwd=4324"]]];

    // 启动任务
    [task resume];
}

#pragma mark - 

// 1.接收到服务器的响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    // 允许处理服务器的响应,才会继续接收服务器返回的数据
    completionHandler(NSURLSessionResponseAllow);
}


// 2.接收到服务器的数据(可能会被调用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    NSLog(@"%s", __func__);
}


// 3.请求成功或者失败(如果失败,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"%s", __func__);
}

使用NSURLSession发送POST请求(代理方式),实现方式和get请求一样,只需要设置请求头

NSURLSessionUploadTask———文件上传

// 设置请求头
request.HTTPMethod = @"POST";
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", XMGBoundary] forHTTPHeaderField:@"Content-Type"];

// 设置请求体
NSMutableData *body = [NSMutableData data];
// 文件参数
// 分割线
[body appendData:XMGEncode(@"--")];
[body appendData:XMGEncode(XMGBoundary)];
[body appendData:XMGNewLine];
.....

// 注意这里通过设置请求体 = data完成文件上传,官方说这样做会被忽略
// 就是说, 如果利用NSURLSessionUploadTask上传文件, 那么请求体必须写在fromData参数中, 不能设置在request中. 否则设置在request中会被忽略
/*
The body stream and body data in this request object are ignored.
*/
// request.HTTPBody = data; ❌

// 1.创建session
NSURLSession *session = [NSURLSession sharedSession];

// 2.根据session创建Task
//注意这里的data是文件参数和非文件参数的拼接二进制❤️
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData: body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
 NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}];

// 3.执行Task
[task resume];

// 注意:不要使用这个方法. fromFile方法是用于PUT请求上传文件的
// 而我们的服务器只支持POST请求上传文件 
[session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {}];❌
文件上传的监听
// 1.创建session
 NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

 // 2.根据session创建Task
 // 该方法中有回调函数,会影响代理方法调用
 // NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { }];

 NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body];

 // 3.执行Task
 [task resume];

// =================代理方法====================
#pragma mark - NSURLSessionTaskDelegate
// 上传过程中调用
/*
bytesSent:当前这一次上传的数据大小;
totalBytesSent:总共上传数据大小
totalBytesExpectedToSend:需要上传的文件大小
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
  NSLog(@"didSendBodyData");
  NSLog(@"%f",1.0 * totalBytesSent/totalBytesExpectedToSend);
}

// 请求完毕时调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{ 
   NSLog(@"didCompleteWithError"); 
}

NSURLSessionDownloadTask ———文件下载任务

注意:
1.默认情况下,使用NSURLSessionDownloadTask时,系统已经帮我们实现边下载边存储.防止内存暴增.
2.而我们只需要把下载的资源从不安全的tmp文件夹挪到caches文件夹.

1. 使用 NSURLSession 对象创建下载请求
2. 在下载请求中移动文件到指定位置
3. 执行请求

//确定请求路径
 NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
 //创建请求对象
 NSURLRequest *request = [NSURLRequest requestWithURL:url];
 //创建会话对象
 NSURLSession *session = [NSURLSession sharedSession];
 //创建会话请求
 //优点:该方法内部已经完成了边接收数据边写沙盒的操作,解决了内存飙升的问题
 NSURLSessionDownloadTask *downTask = [session downloadTaskWithRequest:request 
     completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {

     //默认存储到临时文件夹 tmp 中,需要剪切文件到 cache
     NSLog(@"%@", location);//目标位置
     NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]  
                         stringByAppendingPathComponent:response.suggestedFilename];

     /**
      fileURLWithPath:有协议头
      URLWithString:无协议头
      */
     [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];

 }];
 //发送请求
 [downTask resume];

  • NSURLSessionDownloadDelegate代理方法
// 接受到下载数据时调用[在该方法监听文件下载的进度],此方法会被调用多次
/*
totalBytesWritten:已经写入到文件中的数据大小
 totalBytesExpectedToWrite:目前文件的总大小
 bytesWritten:本次下载的文件数据大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
// 获取文件下载进度
CGFloat progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite; NSLog(@"%f",progress);
}

// 下载完成调用
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didFinishDownloadingToURL:(NSURL *)location {
    // location还是一个临时路径,需要自己挪到需要的路径(caches下面)
    NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:filePath] error:nil];
}

/*
 恢复下载的时候调用该方法
 fileOffset:恢复之后,要从文件的什么地方开发下载
 expectedTotalBytes:该文件数据的总大小
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{

}

// 任务完成调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {

}
  • dataTask 和 downloadTask 下载对比
    • NSURLSessionDataTask
      下载文件可以实现离线断点下载,但是代码相对复杂
    • NSURLSessionDownloadTask
      下载文件可以实现断点下载,但不能离线断点下载
      内部已经完成了边接收数据边写入沙盒的操作
      解决了下载大文件时的内存飙升问题

NSURLSession优点

  • 后台上传和下载:只需在创建NSURLSession的时候配置一个选项,就能得到后台网络的所有好处。这样可以延长电池寿命,并且还支持UIKit的多task,在进程间使用相同的委托模型。
    能够暂停和恢复网络操作:使用NSURLSession API能够暂停,停止,恢复所有的网络任务,再也完全不需要子类化NSOperation.

  • 可配置的容器:对于NSURLSession里面的requests来说,每个NSURLSession都是可配置的容器。举个例来说,假如你需要设置HTTP header选项,你只用做一次,session里面的每个request就会有同样的配置。

  • 提高认证处理:认证是在一个指定的连接基础上完成的。在使用NSURLConnection时,如果发出一个访问,会返回一个任意的request。此时,你就不能确切的知道哪个request收到了访问。而在NSURLSession中,就能用代理处理认证。

  • 丰富的代理模式:在处理认证的时候,NSURLConnection有一些基于异步的block方法,但是它的代理方法就不能处理认证,不管请求是成功或是失败。在NSURLSession中,可以混合使用代理和block方法处理认证。

  • 上传和下载通过文件系统:它鼓励将数据(文件内容)从元数据(URL和settings)中分离出来。

你可能感兴趣的:(初识NSURLSession)