白水

iOS 模块详解—「数据请求:NSURLSession 会话」

引导


苹果在 iOS9 之后已经废弃了 NSURLConnection,所以在现在的实际开发中,最常使用的数据请求就是 AFN 框架,但不能止步于使用AFN,其实AFN内部也是对 NSURLSession 的封装,所以为了更好的理解afn 源码,学习 NSURLSession(iOS7) 是必不可少的。

优势:
NSURLSession 对Foundation URL加载系统进行了彻底的重构,提供了更丰富的API来处理网络请求,如:支持http2.0协议、直接把数据下载到磁盘、同一session发送多个请求、下载是多线程异步处理和提供全局的session并可以统一配置等;

本篇文章主要从 [NSURLSession .h文件 / 场景思维 / 总结笔记] 整理,该模块将系统化学习,后续替换、补充文章内容 ~
在「时间 & 知识 」有限内,总结的文章难免有「未全、不足 」的地方,还望各位好友指出槽点,以提高文章质量@CoderLN著;

目录:

  1. NSURLSession 释义
  2. NSURLSession 组成
  3. NSURLSession 层次结构
    1.相关层次结构
    2.层次结构图解
    3.NSURLSession
    4.dataTask 数据请求
    5.uploadTask 上传请求
    6.downloadTask 下载请求
    7.streamTask 流任务
    8.SessionConfiguration 配置信息
  4. 场景思维
    1.后台传输(采用 downloadTask)
    2.断点续传(采用 dataTask)
  5. 总结笔记
    1.基本使用(GET、POST、下载、上传)
    2.调用原理(DataTaskDelegate、DownloadTaskDelegate)
  6. SourceCodeToolsClassWechatPublic-Codeidea

释义

释义:协调一组相关网络数据传输任务的对象。

组成

NSURLSession 相关的类:

  • NSURLSessionTask:任务,是一个抽象类,本身不能使用只能使用它的子类。
  • NSURLSessionDelegate:代理
  • NSURLSessionConfiguration:配置信息
  • NSURLSessionTaskMetrics:对发送请求的时间统计,内部装着许多 transactionMetrics 数组;
  • NSURLSessionTaskTransactionMetrics:一个task从发出请求到收到数据过程中派生出的每个子请求;

图解:

层次结构

NSURLSession 相关层次结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
#warning - 以下为功能模块相关的方法示例, 具体方法作用、使用、注解请移步 -> github.com/CoderLN/Framework-Codeidea


NSURLSessionTask 任务;是一个抽象类,本身不能使用只能使用它的子类
1.@interface NSURLSessionDataTask : NSURLSessionTask
注解:负责数据请求(GET、POST)、离线断点下载 [返回NSData:响应体信息、NSURLResponse:响应头信息]

@interface NSURLSessionUploadTask : NSURLSessionDataTask
注解:负责上传请求 [返回NSData:响应体信息、NSURLResponse:响应头信息]

2.@interface NSURLSessionDownloadTask : NSURLSessionTask
注解:负责下载请求 [返回临时文件URL路径tmp, 需要剪切文件把它移动(moveItemAtURL:)到我们指定的位置]

3.@interface NSURLSessionStreamTask : NSURLSessionTask
注解:流任务,用于建立一个TCP/IP连接

- - -
- - -

NSURLSessionDelegate 代理

@protocol NSURLSessionDelegate <NSObject>
@protocol NSURLSessionTaskDelegate <NSURLSessionDelegate>
@protocol NSURLSessionDataDelegate <NSURLSessionTaskDelegate>
@protocol NSURLSessionDownloadDelegate <NSURLSessionTaskDelegate>
@protocol NSURLSessionStreamDelegate <NSURLSessionTaskDelegate>


- - -
- - -


NSURLSessionConfiguration 配置信息
1.defaultSessionConfiguration; 默认配置 (使用磁盘缓存,用将证书存在用户的钥匙串)
2.ephemeralSessionConfiguration; 预设配置 (只做内存缓存不做磁盘缓存,也存储证书)
3.backgroundSessionConfigurationWithIdentifier: 后台配置 (需要指定一个identifier用来后台重连session对象,且有一个独立进程来操作上传/下载)


- - -
- - -

NSURLSessionTaskMetrics & NSURLSessionTaskTransactionMetrics
1.对发送请求/DNS查询/TLS握手/请求响应等各种环节时间上的统计. 更易于我们检测, 分析我们App的请求缓慢到底是发生在哪个环节, 并对此进行优化提升我们APP的性能.

2.NSURLSessionTaskMetrics对象与NSURLSessionTask对象一一对应. 每个NSURLSessionTaskMetrics对象内有3个属性 :
taskInterval : task从开始到结束总共用的时间
redirectCount : task重定向的次数
transactionMetrics : 一个task从发出请求到收到数据过程中派生出的每个子请求, 它是一个装着许多NSURLSessionTaskTransactionMetrics对象的数组.
3.API就一个方法 - (void)URLSession: task: didFinishCollectingMetrics:, 当收集完成的时候就会调用该方法.
*/

层次结构图解:

NSURLSession
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
NSURLSession
#warning - 以下为功能模块相关的方法示例, 具体方法作用、使用、注解请移步 -> github.com/CoderLN/Framework-Codeidea


#pragma mark - Session创建

sharedSession;// 初始化单例
+ sessionWithConfiguration:// 创建一个指定配置的session
+ sessionWithConfiguration:delegate:delegateQueue:// 创建一个指定配置, 代理和代理方法执行队列的session

- - -

#pragma mark - Session属性

NSOperationQueue *delegateQueue;// 代理方法执行的队列
id <NSURLSessionDelegate> delegate;// 代理
NSURLSessionConfiguration *configuration;// 配置信息
NSString *sessionDescription;// app定义的对于该session的描述

- - -

#pragma mark - ↑
#pragma mark - 管理session

- finishTasksAndInvalidate;// 任务全部完成后销毁session 释放掉
- invalidateAndCancel;// 取消所有未完成的任务并销毁session
- resetWithCompletionHandler:// 清空cookies缓存和证书存储, 移除所有磁盘文件清理正在执行的下载任务,确保未来能响应一个新的socket请求
- flushWithCompletionHandler:// 清除硬盘上的cookies和证书, 清理暂时的缓存, 确保未来能响应一个新的TCP请求
- getTasksWithCompletionHandler:// 异步调用session中所有upload, download, data tasks的completion回调.

- - -

#pragma mark - ↑
#pragma mark - SessionDelegate (主要处理鉴权、后台下载任务完成通知等)

- URLSession:didBecomeInvalidWithError:// 当一个session遇到系统错误或者未检测到的错误的时候,就会调用这个方法
- URLSession:didReceiveChallenge:completionHandler:// 当一个服务器请求身份验证或TLS握手期间需要提供证书的话
- URLSessionDidFinishEventsForBackgroundURLSession:// 如果应用进入后台、这个方法会被调用。我们在这里可以对session发起的请求做各种操作比如请求完成的回调等
*/
dataTask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
#warning - 以下为功能模块相关的方法示例, 具体方法作用、使用、注解请移步 -> github.com/CoderLN/Framework-Codeidea


#pragma mark - 添加dataTask任务

- dataTaskWithRequest:// 获取指定URLRequest内容
- dataTaskWithRequest:completionHandler:// 获取指定URLRequest内容, 在completionHandler中处理数据.
- dataTaskWithURL:// 获取指定URL内容
- dataTaskWithURL:completionHandler:// 获取指定URL内容, 在completionHandler中处理数据.

- - -
- - -

#pragma mark - DataDelegate (主要处理数据的接收、dataTask转downloadTask、缓存等)

- URLSession:task:didCompleteWithError:// 任何task完成(成功 或 失败)的时候都会调用,error有可能为nil(请求成功), 不为nil(请求失败)

- (void)URLSession:dataTask:didReceiveResponse:completionHandler:// 1.接收到服务器的响应(默认会取消网络请求);参数:response.expectedContentLength本次请求的数据大小
- URLSession:dataTask:didBecomeDownloadTask:// 当一个datatask转换为一个downloadtask以后会调用
- URLSession:dataTask:didBecomeStreamTask:// 这个是和数据流相关的
- URLSession:dataTask:didReceiveData:// 2.接收到服务器返回的数据(调用多次)
- URLSession:dataTask:willCacheResponse:completionHandler:// 允许我们在这里调用completionHandler缓存data,或者传入nil来禁止缓存
*/
uploadTask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
#warning - 以下为功能模块相关的方法示例, 具体方法作用、使用、注解请移步 -> github.com/CoderLN/Framework-Codeidea


#pragma mark - 添加uploadTask任务

- uploadTaskWithStreamedRequest:// 通过HTTP请求发送指定URLRequest数据流给指定URL
- uploadTaskWithRequest:fromFile:// 通过HTTP请求发送指定文件给指定URL
- uploadTaskWithRequest:fromFile:completionHandler:// 通过HTTP请求发送指定文件给指定URL, 在completionHandler中处理数据. 该方法会绕过代理方法(除了身份认证挑战的代理方法)
- uploadTaskWithRequest:fromData:// 通过HTTP请求发送data给指定URL
- uploadTaskWithRequest:fromData:completionHandler:// 通过HTTP请求发送data给指定URL, 在completionHandler中处理数据.

- - -
- - -

- URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:// 监听上传进度;参数:上传完成的数据大小、文件的总大小 (上传进度 1.0 * totalBytesSent / totalBytesExpectedToSend)
*/
downloadTask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
#warning - 以下为功能模块相关的方法示例, 具体方法作用、使用、注解请移步 -> github.com/CoderLN/Framework-Codeidea


#pragma mark - 添加downloadTask任务

- downloadTaskWithRequest:// 下载指定URLRequest内容
- downloadTaskWithRequest:completionHandler:// 下载指定URLRequest内容, 在completionHandler中处理数据.
- downloadTaskWithURL:// 下载指定URL内容
- downloadTaskWithURL:completionHandler:// 下载指定URL内容, 在completionHandler中处理数据.
- downloadTaskWithResumeData:// 创建一个之前被取消/下载失败的download task
- downloadTaskWithResumeData:completionHandler:// 创建一个之前被取消/下载失败的download task, 在completionHandler中处理数据. 该方法会绕过代理方法(除了身份认证挑战的代理方法)

- - -
- - -

#pragma mark - DownloadDelegate (主要处理数据下载、数据进度通知等)

- URLSession:downloadTask:didFinishDownloadingToURL:// 当下载完成的时候调用,需要剪切临时文件路径把它移动(moveItemAtURL:)到我们指定的位置
- URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:// 写数据(监听下载进度); 参数:下载的数据总大小、文件的总大小 (下载进度 1.0 * totalBytesWritten/totalBytesExpectedToWrite)
- URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:// 当恢复下载的时候调用方法;参数:从什么地方下载、文件的总大小
*/
streamTask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
#warning - 以下为功能模块相关的方法示例, 具体方法作用、使用、注解请移步 -> github.com/CoderLN/Framework-Codeidea


#pragma mark - 添加streamTask任务

- streamTaskWithHostName:port: ios(9.0)// 通过给定的域名和端口建立双向TCP/IP连接
- streamTaskWithNetService: ios(9.0)// 通过给定的network service建立双向TCP/IP连接

- - -
- - -

- URLSession:task:(NSURLSessionTask *)needNewBodyStream:// 如果任务的数据是由一个stream发出的, session就会调用该方法获取一个NSInputStream对象并提供一个新请求的body data.
*/
SessionConfiguration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
#warning - 以下为功能模块相关的方法示例, 具体方法作用、使用、注解请移步 -> github.com/CoderLN/Framework-Codeidea


NSURLSessionConfiguration 配置信息
1.defaultSessionConfiguration; 默认配置 (使用磁盘缓存,用将证书存在用户的钥匙串)
2.ephemeralSessionConfiguration; 预设配置 (只做内存缓存不做磁盘缓存,也存储证书)
3.backgroundSessionConfigurationWithIdentifier: 后台配置 (需要指定一个identifier用来后台重连session对象,且有一个独立进程来操作上传/下载)

- - -

NSString *identifier;// 标识
NSURLRequestCachePolicy requestCachePolicy;//请求缓存策略
NSTimeInterval timeoutIntervalForRequest; //请求超时
NSTimeInterval timeoutIntervalForResource;//响应超时
BOOL allowsCellularAccess; //是否允许蜂窝网络下的数据请求
BOOL discretionary (ios(7.0))// 允许后台任务在性能最优状态下进行(当电量不足或者是蜂窝时将不进行,后台任务推荐设置该属性)
*/

场景思维

后台传输

需求:实现文件的 断点|离线|后台 下载
解决:这里采用 downloadTask

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#pragma mark --- 后台下载
1.
typedef void(^completionHandle)();
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, copy) completionHandle handle;// 保存后台任务完成回调

2.
// 在应用处于后台,且后台任务下载完成时回调
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler
{
NSLog(@"1 %s",__FUNCTION__);
// 将下载完成后的数据回调保存
self.handle = completionHandler;
}

3.
- (NSURLSession *)backgroundSession
{
static NSURLSession *_backgroundSession = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.Jerry4me.backgroundSessionIdentifier"];
_backgroundSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
});

return _backgroundSession;
}

4.
// 应用在后台,而且后台所有下载任务完成后 (可以在该方法中做下载数据管理和UI刷新)
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
NSLog(@"4 %s",__FUNCTION__);
// 获取Application的代理
AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

if (delegate.handle) {
completionHandle handle = delegate.handle;
delegate.handle = nil;

// 需要回到主线程调用
dispatch_async(dispatch_get_main_queue(), ^{
handle();
});
}
}

- - -
- - -

注解:后台传输 (进入后台下载完成后方法调用顺序)

DataRequest[1209:129165] 0.688557
DataRequest[1209:129165] 0.692577
DataRequest[1209:129165] 1 -[AppDelegate application:handleEventsForBackgroundURLSession:completionHandler:]
DataRequest[1209:129165] 2 -[DownloadTaskViewController URLSession:downloadTask:didFinishDownloadingToURL:]
DataRequest[1209:129165] /var/mobile/Containers/Data/Application/E705F375-B90C-4549-9048-61D4B904D54B/Library/Caches/minion_01.mp4
DataRequest[1209:129165] 3 -[DownloadTaskViewController URLSession:task:didCompleteWithError:]
DataRequest[1209:129165] 4 -[DownloadTaskViewController URLSessionDidFinishEventsForBackgroundURLSession:]

总结:对这个过程进行一些针对性的优化
最好把文件先压缩成zip/tar等压缩文件再上传/下载.
把大文件按数据段分别发送, 发送完之后服务端再把数据拼接起来.
上传的时候服务端应该返回一个标识符, 这样可以追踪传输的状态, 及时做出传输的调整
增加一个web代理服务器中间层, 以促进上述的优化

断点续传
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

downloadTask 断点续传
1.下载失败/暂停/被取消, 可以通过task的- cancel​By​Producing​Resume​Data:​方法保存已下载的数据
2.然后调用session的 download​Task​With​Resume​Data:​方法, 触发代理的URLSession:​download​Task:​did​Resume​At​Offset:​expected​Total​Bytes:​方法


dataTask 断点续传
1.设置请求头信息,告诉服务器请求那一部分数据
[request setValue:[NSString stringWithFormat:@"bytes=%zd-",self.currentSize] forHTTPHeaderField:@"Range"];

2.创建文件句柄NSFileHandle, 移动指针(每接收到服务器的响应,就移动指针指向文件末尾)
[self.handle seekToEndOfFile];

注解:销毁session
// 如果设置代理的话会有一个强引用不会被释放掉,当不用Session的时候
// 一定要调用finishTasksAndInvalidate(任务全部完成后销毁session) 和 invalidateAndCancel(取消所有未完成的任务并销毁session)
-(void)dealloc {
[self.session invalidateAndCancel];
}

总结笔记

基本使用

NSURLSession

  • 1、NSURLSessionDataTask 据请求(GET、POST)
  • 2、NSURLSessionDownloadTask 下载请求
  • 3、NSURLSessionUploadTask 上传请求

1、示例:DataTask 据请求(POST)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#pragma mark - DataTask GET、POST请求使用步骤:
/**
DataTask GET、POST请求使用步骤:
1.确定url
2.创建请求对象 request
3.创建会话对象 Session (单例创建 或 自定义配置文件,设置代理)
4.Session创建下载任务Task (dataTaskWithRequest:)
5.执行Task resume
6.解决设置代理之后的强引用问题,在dealloc中销毁session (调用finishTasksAndInvalidate | invalidateAndCancel)
7.POST方法还可以设置一下属性
request.HTTPMethod 设置请求方法
request.HTTPBody 设置请求体
request.timeoutInterval 设置请求超时
setValue:forHTTPHeaderField: 设置请求头(key一定要一致用于传递数据给后台)
8.注解:
1.cancel 取消是不能恢复
*/
- - -
- - -

- (void)post {

// 1.确定url
NSURL *url = [NSURL URLWithString:urlStr];

// 2.创建请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

// 3.创建会话对象Session(单例)
NSURLSession *session = [NSURLSession sharedSession];
// 设置请求方法
request.HTTPMethod = @"POST";
// 设置请求体
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
// 设置请求超时
//request.timeoutInterval = 10;

// 4.session创建Task
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// 6.解析数据
NSString *result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@",result);

[self showWithResult:result];// HUD弹框提示登录结果
}];

// 5.执行Task
[dataTask resume];
}

2、示例:DownloadTask 下载请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
DownloadTask 文件下载 使用步骤:
1.确定url
2.创建会话对象 Session (自定义配置文件,设置代理)
3.Session创建下载任务task (downloadTaskWithRequest:)
4.执行Task resume
5.- URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:// 写数据(监听下载进度); 参数:下载的数据总大小、文件的总大小 (下载进度 1.0 * totalBytesWritten/totalBytesExpectedToWrite)
6.- URLSession:downloadTask:didFinishDownloadingToURL:// 当下载完成的时候调用,需要剪切临时文件路径把它移动(moveItemAtURL:)到我们指定的位置
7.- URLSession:task:didCompleteWithError:// 任何task完成(成功 或 失败)的时候都会调用,error有可能为nil(请求成功), 不为nil(请求失败)
8.注解:
1.边接受数据边写入沙盒(tmp临时文件目录)的操作
2.cancel 取消是不能恢复
3.cancelByProducingResumeData: 是可以恢复
*/

- - -
- - -

- (void)download2 {
// 1.确定URL
NSURL *url = [NSURL URLWithString:urlStr];

// 2.创建会话对象 (自定义配置文件,设置代理)
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

// 3.session创建downloadtask
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:[NSURLRequest requestWithURL:url]];

// 4.执行downloadtask
[downloadTask resume];
}


#pragma mark - NSURLSessionDownloadDelegate

/**
1.当恢复下载的时候调用方法
fileOffset 从什么地方下载
expectedTotalBytes 文件的总大小
*/
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {
NSLog(@"恢复下载--%s",__func__);
}

/**
2.写数据(监听下载进度)
session 会话对象
downloadTask 下载任务
bytesWritten 本次写入的数据大小
totalBytesWritten 下载的数据总大小
totalBytesExpectedToWrite 文件的总大小
*/
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {

// 获得文件的下载进度
NSLog(@"下载进度 == %f",1.0 * totalBytesWritten/totalBytesExpectedToWrite);
self.proessView.progress = 1.0 * totalBytesWritten/totalBytesExpectedToWrite;
}



/**
3.当下载完成的时候调用
location 文件的临时存储路径tmp, 需要剪切文件把它移动(NSFileManager moveItemAtURL:)到我们指定的位置
*/
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
NSLog(@"2 %s",__FUNCTION__);
// NSLog(@"文件的临时存储路径--%@",location);

// 1.拼接文件全路径
// downloadTask.response.suggestedFilename 文件名称
NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
// 2.剪切文件
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
UIImage *image = [UIImage imageWithContentsOfFile:fullPath];

dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
NSLog(@"%@",fullPath);
}


/**
4.请求成功 或 失败 都会调用
*/
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {

NSLog(@"3 %s",__FUNCTION__);
if (error) {
NSLog(@"请求失败 %@",error);
} else {
NSLog(@"请求成功 %@",task.response);
}
}

3、UploadTask 上传请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
UploadTask上传文件 使用步骤:
1.确定URL
2.创建请求对象request
request.HTTPMethod = @"POST";设置请求方式
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"content-type"];设置请求头
3.创建会话对象
sharedSession // 基本上传
sessionWithConfiguration:config delegate:self delegateQueue: // 监听上传进度, 设置代理;
config.allowsCellularAccess = YES;// 访问蜂窝数据
config.timeoutIntervalForRequest = 10;// 请求超时
4.session创建uploadTask
request 请求对象,【fromData 请求体】, data 响应体 , response 响应头, error 错误信息
uploadTaskWithRequest:request fromData:[self getHttpBodyData] completionHandler:
5.执行uploadTask resume
6.监听上传进度 - URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:// 监听上传进度;参数:上传完成的数据大小、文件的总大小 (上传进度 1.0 * totalBytesSent / totalBytesExpectedToSend)
*/

- - -
- - -

- (void)uploadTask
{
// 1.确定URL
NSURL * url = [NSURL URLWithString:urlStr];

// 2.创建请求对象
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
// 设置请求方式
request.HTTPMethod = @"POST";
// 设置请求头
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"content-type"];

// 3.创建会话对象
//NSURLSession *session = [NSURLSession sharedSession]; // 基本上传
//NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]]; // 监听上传进度, 设置代理;

// 4.session创建task
// request 请求对象,【fromData 请求体】, data 响应体 , response 响应头, error 错误信息
NSURLSessionUploadTask * uploadTask = [self.session uploadTaskWithRequest:request fromData:[self getHttpBodyData] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

NSString *d = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}];

// 5.执行task
[uploadTask resume];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
NSLog(@"上传进度 == %f",1.0 * totalBytesSent / totalBytesExpectedToSend);
}
调用原理
  • DataTask Delegate
1
2
3
4
1.对于一个data task来说, session会调用代理的URLSession:​data​Task:​did​Receive​Response:​completion​Handler:​方法, 决定是否将一个data dask转换成download task, 然后调用completion回调继续接收data或下载data.
2.如果你的app选择转换成download task, session会调用代理的URLSession:​data​Task:​did​Become​Download​Task:​方法并把新的download task对象以方法参数的形式传给你. 之后代理不会再收到data task的回调而是转为收到download task的回调
3.在服务器传输数据给客户端期间, 代理会周期性地收到URLSession:​data​Task:​did​Receive​Data:​回调
4.session会调用URLSession:​data​Task:​will​Cache​Response:​completion​Handler:​询问你的app是否允许缓存. 如果代理不实现这个方法的话, 默认使用session绑定的Configuration的缓存策略.
  • downloadTask Delegate
1
2
3
4
5
1.对于一个通过download​Task​With​Resume​Data:​创建的下载任务, session会调用代理的URLSession:​download​Task:​did​Resume​At​Offset:​expected​Total​Bytes:​方法.
2.在服务器传输数据给客户端期间, 调用URLSession:​download​Task:​did​Write​Data:​total​Bytes​Written:​total​Bytes​Expected​To​Write:给用户传数据
1.当用户暂停下载时, 调用cancel​By​Producing​Resume​Data:​给用户传已下好的数据.
2.如果用户想要恢复下载, 把刚刚的resumeData以参数的形式传给download​Task​With​Resume​Data:​方法创建新的task继续下载.
3.如果download task成功完成了, 调用URLSession:​download​Task:​did​Finish​Downloading​To​URL:把临时文件的URL路径给你. 此时你需要剪切文件把它移动(NSFileManager moveItemAtURL:)到我们指定的位置fullPath.

Reading


  • 如果在阅读过程中遇到 Error || New ideas,希望你能 issue 我,我会及时补充谢谢。
  • 熬夜写者不易,喜欢可 赞赏 or Star 一波;点击左上角关注 或 『Public:Codeidea』,在 Demo | 文章 | Rss 更新时收到提醒通知,便捷阅读。
既然阅读完了    就在下面留言吧!

标题:iOS 模块详解—「数据请求:NSURLSession 会话」

作者:白水

链接:http://githubidea.github.io/iOSNET/NSURLSession.html

版权声明: 署名-非商业性使用-禁止演绎 4.0 国际 本博客文章除特别声明外均为原创,如需转载请务必保留原链接(可点击进入的链接)和 作者出处。