iOS —— NNSURLSessionDataTask

一、基本简介

1. NSURLSessionDataTask 是 NSURLSessionTask 的子类,是一个具体的 网络请求(task) 类,是网络请求中最常用的请求之一

通常,NSURLSessionDataTask 用来请求数据,可以用来下载数据资源,例如 JSON 数据,图片数据等

2. 通常有以下几种方法创建一个 data task

1)方法一 : 使用 NSURLSession 的实例方法

1 // @param url 待请求的 URL 地址
2 // @param completionHandler 回调方法
3 // @param data 从服务器请求到的数据
4 // @param response 响应头信息
5 // @param error 错误信息
6 - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;

注意 :

该方法中会自动将 url 转换为一个请求对象(NSURLRequest),并且该请求对象是 GET 请求方式

回调方法是在子线程中运行的,所以如果在回调方法中刷新 UI 必须回到主线程中

回调方法中有两个参数 response / error,这两个参数和 该消息的接受者(即 NSURLSessionDataTask 对象)中的 response / error 是指同一个内容,即地址相同

使用该方法的缺点是不能监听获取数据的进度,因为只有当全部请求完数据后,才会调用这个方法,也就是说,data 中的数据是请求的全部数据

2)方法二 : 使用 NSURLSession 的实例方法

// @param request 请求对象
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error))completionHandler;

方法二与方法一不同的地方在于 : 方法二可以手动设置请求对象,这样一来,就可以指定请求方式 GET/POST 中的一个;而方法一只能是 GET 请求方式

剩余的全部一样

3)方法三 : 代理

方法一和方法二唯一的缺点就是不能监控请求进度,因为只有当请求完全部的数据后才会调用回调方法,如果想要监控请求进度,必须使用代理的方法

使用代理首先要自定义 NSURLSession 对象,使用下面的方法可以设置代理对象

// @param configuration 配置信息对象
// @param delegate 代理对象
// @param queue 代理方法在哪个线程中运行,如果传 nil 则会在子线程中运行代理方法
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;

同时,必须遵守相关的协议

在使用下面的方法创建 data task

- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;

这两个方法的区别和方法一/方法二一样,使用 url 则方法内部会自动将其转换为一个 请求对象,并且是 GET 请求方式

二、示例代码

因为我是把所有的代码写到一个 demo 里的,所以有些目前不需要的东西可以不必理会

1)GET/POST 请求

首先在 main.storyboard 中拖入一个 UINavigationController ,并设置 static cell,如图

然后拖入一个 UIViewController 并选中第一个 cell,即 NSURLSessionDataTask,将 cell 和 UIViewController 连线,选择 push

在这个 UIViewController 中拖入一系列控件,如图

右上角的 UIBarButtonItem 是跳转到下一个界面的,不用管它

UIViewController 中的代码如下

 1 #import "LHDataTaskViewController.h"
 2
 3 // GET 请求的 URL
 4 static NSString * imageURL = @"http://120.25.226.186:32812/resources/images/minion_01.png";
 5
 6 // POST 请求的 URL
 7 static NSString * dataURL = @"http://api.hudong.com/iphonexml.do";
 8
 9 @interface LHDataTaskViewController ()
10
11 #pragma mark - 属性
12 @property (weak, nonatomic) IBOutlet UIImageView *showImageView;
13
14 @property (nonatomic, strong) NSURLSession * session;
15 @property (nonatomic, strong) NSURLSessionDataTask * dataTask;
16
17 @end
18
19 @implementation LHDataTaskViewController
20
21 #pragma mark - ViewController 生命周期
22 - (void)viewDidLoad {
23
24     [super viewDidLoad];
25
26     // 1. 初始化 NSURLSession 对象
27     self.session = [NSURLSession sharedSession];
28
29 }
30
31 - (void)didReceiveMemoryWarning {
32
33     [super didReceiveMemoryWarning];
34
35 }

1. GET 请求

设置 "GET请求" 按钮的 动作方法

 1 #pragma mark - button 动作方法
 2 #pragma mark 发送 GET 请求获取图片资源
 3 - (IBAction)GETButtonClick:(id)sender {
 4
 5     NSLog(@"dataTask的状态 --- %li", _dataTask.state);
 6
 7     // 1. 初始化 NSURLSesionDataTask 对象
 8     self.dataTask = [_session dataTaskWithURL:[NSURL URLWithString:imageURL] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
 9
10         // 1). 定义 UIImage 对象,并用接受的数据(data)初始化
11         UIImage * image = [UIImage imageWithData:data];
12
13         // 2). 返回主线程刷新UI
14         dispatch_async(dispatch_get_main_queue(), ^{
15
16             self.showImageView.image = image;
17
18         });
19
20         NSLog(@"dataTask的状态 --- %li", _dataTask.state);
21
22         // 此时,所有数据已经全部接受完毕,所以,已经接受的的数据和所要接受的总数据是相等的
23         // 因为没有发送数据,所以发送数据都为 0
24         NSLog(@"已接受到的数据量 --- %lli", _dataTask.countOfBytesReceived); // 48347
25         NSLog(@"所要接受到的数据总量 --- %lli", _dataTask.countOfBytesExpectedToReceive); // 48347
26         NSLog(@"已经发送的数据量 --- %lli", _dataTask.countOfBytesSent); // 0
27         NSLog(@"所要发送的数据总量 --- %lli", _dataTask.countOfBytesExpectedToSend); // 0
28
29     }];
30
31     // 2. 发送请求,执行 task
32     [_dataTask resume];
33
34 }

其中,24行 —— 27行 中用到的属性,在上一节已经介绍过

注意 : NSURLSessionTask 中所有的 task 都需要 resume 来开始

2. POST 请求

设置 "POST请求" 按钮的动作方法

 1 - (IBAction)POSTButtonClick:(UIButton *)sender {
 2
 3     // 1. 创建请求对象(可变)
 4     NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:dataURL]];
 5
 6     // 2. 设置请求方法为 POST 请求
 7     request.HTTPMethod = @"POST";
 8
 9     request.HTTPBody = [@"type=focus-c" dataUsingEncoding:NSUTF8StringEncoding];
10
11     // 1. 初始化 NSURLSessionDataTask 对象
12     self.dataTask = [_session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
13
14         NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
15
16     }];
17
18     [_dataTask resume];
19
20 }

2. 代理

使用代理的方法来进行网络请求,并且可以监控请求进度

现在 mian.storyboard 中拖入一个 UIViewController 并添加控件,如图

本次请求的是一张图片数据,请求完之后会将图片显示到屏幕上的 UIImageView,resume 按钮是开始请求,pause 按钮是暂停请求,cance 按钮是取消请求,中间还有一个 UIProgressView,用于显示请求的进度,并将这些控件与插座变量关联

UIViewController 中的代码如下,该 ViewController 要遵守相关协议 <NSURLSessionDataDelegate>

 1 #import "LHDataTaskDownloadViewController.h"
 2
 3 // 待访问的 URL
 4 static NSString * imageURL = @"http://f12.topit.me/o129/10129120625790e866.jpg";
 5
 6 @interface LHDataTaskDownloadViewController () <NSURLSessionDataDelegate>
 7
 8 #pragma mark - 属性列表
 9 #pragma mark 插座变量
10 @property (weak, nonatomic) IBOutlet UIImageView *showImageView;
11 @property (weak, nonatomic) IBOutlet UIProgressView *progressView;
12 @property (weak, nonatomic) IBOutlet UIButton *resumeButton;
13 @property (weak, nonatomic) IBOutlet UIButton *pauseButton;
14 @property (weak, nonatomic) IBOutlet UIButton *cancelButton;
15
16 #pragma mark 网络对象
17 @property (nonatomic, strong) NSURLSession * session;
18 @property (nonatomic, strong) NSURLSessionDataTask * dataTask;
19
20 #pragma mark 用于接受数据的对象
21 @property (nonatomic, strong) NSMutableData * data;
22
23 @end
24
25 @implementation LHDataTaskDownloadViewController
26
27 - (void)viewDidLoad {
28
29     [super viewDidLoad];
30
31     // 1. 初始化 NSURLSession 对象,delegateQueue 为协议方法运行的线程,传 nil 则在子线程中运行
32     self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
33
34     // 2. 初始化 NSURLSessionDataTask 对象,默认为 GET
35     self.dataTask = [_session dataTaskWithURL:[NSURL URLWithString:imageURL]];
36
37     // 2. 将 cancelButton 和 pauseButton 按钮设置为不可用
38     _cancelButton.enabled = NO;
39
40     _pauseButton.enabled = NO;
41
42 }

开始按钮的动作方法

 1 - (IBAction)resumeButtonClick:(id)sender {
 2
 3     // 1. 判断当前的状态,data task 默认为 暂停状态
 4     if (_dataTask.state == NSURLSessionTaskStateSuspended) {
 5
 6         _pauseButton.enabled = YES;
 7
 8         _cancelButton.enabled = YES;
 9
10         // 1). 开始请求 task
11         [_dataTask resume];
12
13     }
14
15 }

暂停按钮的动作方法

 1 - (IBAction)pauseButtonClick:(id)sender {
 2
 3     // 1. 判断 task 当前的状态,如果处于正在接受数据的状态,则暂停
 4     if (_dataTask.state == NSURLSessionTaskStateRunning) {
 5
 6         [_dataTask suspend];
 7
 8     }
 9
10 }

取消按钮的动作方法

 1 - (IBAction)cancelButtonClick:(id)sender {
 2
 3     // 1. 判断 task 当前的状态,如果处于正在接受数据的状态或暂停状态,则取消
 4     if (_dataTask.state == NSURLSessionTaskStateRunning || _dataTask.state == NSURLSessionTaskStateSuspended) {
 5
 6         // 1). 取消 task
 7         [_dataTask cancel];
 8
 9         // 2). 设置滑动条的值
10         _progressView.progress = 0;
11
12         // 3). 创建对话框VC
13         UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"提示" message:@"该 Task 已经被取消" preferredStyle:UIAlertControllerStyleAlert];
14
15         // 4). 显示对话框VC
16         [self presentViewController:alertVC animated:YES completion:nil];
17
18         // 5). 创建动作按钮
19         UIAlertAction * cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
20
21             [self.navigationController popViewControllerAnimated:YES];
22
23         }];
24
25         // 6). 将动作按钮添加到对话框VC
26         [alertVC addAction:cancelAction];
27
28     }
29
30 }

协议方法

#pragma mark 接收到服务器响应时调用,默认情况下不接受数据,所以要允许
// @param session 当前的会话对象
// @param dataTask 当前的 data task 对象
// @param response 响应头对象
// @param completionHandler 回调
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {

    NSLog(@"%@", NSStringFromSelector(_cmd));

    // 1. 初始化数据对象
    self.data = [[NSMutableData alloc] init];

    // 2. 允许接受数据,如果没有写这句,则后面代理的方法不会被执行
    completionHandler(NSURLSessionResponseAllow);

}

其中,NSURLSessionResponseDisposition 是一个枚举

1 typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) {
2     NSURLSessionResponseCancel = 0,        // 取消接受数据,之后的代理方法不会被执行,相当于 [task cancel];
3     NSURLSessionResponseAllow = 1,         // 允许接受数据,之后的代理方法会被执行
4     NSURLSessionResponseBecomeDownload = 2,// 使当前的 data task 变为 download task,当转换为 download task 时,会将数据下载到 tmp 文件中,不需要再接受数据了,并且必须调用下面的 iii) 方法,并且在该方法中可以什么都不写,但必须被调用
5     NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3,// 使当前的 data task 变为 stream task
6 } NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);
 1 #pragma mark 接受到数据时调用,可能会调用多次
 2 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
 3
 4     NSLog(@"%@", NSStringFromSelector(_cmd));
 5
 6     // 1. 拼接收到的数据
 7     [self.data appendData:data];
 8
 9     // 2. 回到主线程刷新 UI
10     dispatch_async(dispatch_get_main_queue(), ^{
11
12         _progressView.progress = (float)_dataTask.countOfBytesReceived/_dataTask.countOfBytesExpectedToReceive;
13
14     });
15
16 }
 1 #pragma mark 请求结束或失败时调用
 2 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
 3
 4     NSLog(@"%@", NSStringFromSelector(_cmd));
 5
 6     UIImage * image = [UIImage imageWithData:_data];
 7
 8     dispatch_async(dispatch_get_main_queue(), ^{
 9
10         NSLog(@"currentThread --- %@", [NSThread currentThread]);
11
12         self.showImageView.image = image;
13
14     });
15
16 }

这个方法并不是 NSURLSessionTaskDelegate 协议中的方法,适合所有的 task

时间: 2024-08-25 15:04:59

iOS —— NNSURLSessionDataTask的相关文章

iOS -- SKSpriteNode类

SKSpriteNode类 继承自 SKNode:UIResponder:NSObject 符合 NSCoding(SKNode)NSCopying(SKNode)NSObject(NSObject) 框架  /System/Library/Frameworks/SpriteKit.framework 可用性 可用于iOS 7.0或者更晚的版本 声明于 SKSpriteNode.h 参考指南 Sprite Kit Progamming Guide 概览 重要提示:这是一个初步的API或者开发技术

使用fruitstrap实现命令行将IPA包安装到iOS设备上

Requirements Mac OS X. Tested on Snow Leopard only. You need to have a valid iPhone development certificate installed. Xcode must be installed, along with the SDK for your iOS version. Usage fruitstrap [-d] -b <app> [device_id] Optional -d flag laun

iOS -- SKScene类

SKScene类 继承自 SKEffectNode:SKNode:UIResponder:NSObject 符合 NSCoding(SKNode)NSCopying(SKNode)NSObject(NSObject) 框架  /System/Library/Frameworks/SpriteKit.framework 可用性 可用于iOS 7.0或者更晚的版本 声明于 SKScene.h 参考指南 Sprite Kit Progamming Guide 概览 重要提示:这是一个初步的API或者开

iOS -- SKPhysicsWorld类

SKPhysicsWorld类 继承自 NSObject 符合 NSCodingNSObject(NSObject) 框架  /System/Library/Frameworks/SpriteKit.framework 可用性 可用于iOS 7.0或者更晚的版本 声明于 SKPhysicsWorld.h 参考指南 Sprite Kit Progamming Guide 概览 重要提示:这是一个初步的API或者开发技术文档.虽然已经审阅了本文档的技术准确性,但是它不是最终的版本.本机密信息仅适用于

iOS证书说明和发布

1.首先通过钥匙串访问——证书助理——从证书颁发机构请求证书——填写证书信息(邮箱,常用名称,存储到磁盘)——存储为(自定义名称.certSigningReuqest,简称CSR文件,只是为了提交到苹果开发者账号中,然后就没用了)到本地 2.苹果开发者账号中,创建证书(Development和Production)——上传CSR文件——下载证书运行 ( xxx.cer文件) 注意:只有在当前电脑中生成本地生成证书,上传到苹果开发账号,然后下载cer文件运行后,钥匙串中才有证书以及对应的秘钥 如果

iOS开发——项目实战总结&amp;UITableView性能优化与卡顿问题

UITableView性能优化与卡顿问题 1.最常用的就是cell的重用, 注册重用标识符 如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell 如果有很多数据的时候,就会堆积很多cell.如果重用cell,为cell创建一个ID 每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell 2.避免cell的重新布局 cell的布局填充等操作 比较耗时,一般创建时就布局好 如可以将cell单独放到一个自定义类,初始化时就布局好

解决ios下的微信打开的页面背景音乐无法自动播放

后面的项目发现,还有两个坑,需要注意下: ·本文的解决方案的核心是利用了 微信/易信 在ready的时候会有个 WeixinJSBridgeReady/YixinJSBridgeReady事件,通过监听这个事件来触发的.那有个坑就是 如果微信已经ready了,但还没执行到你监听这个ready事件的代码,那么你的监听是没用的,所以最理想的情况是,监听的js放在head前面(放在css外链之前),确保最新执行,切记!切记!. ·另一个坑就是,本文的解决方案只适合一开始就播放的背景音乐.如果你是做那种

iOS程序执行顺序和UIViewController 的生命周期(整理)

说明:此文是自己的总结笔记,主要参考: iOS程序的启动执行顺序 AppDelegate 及 UIViewController 的生命周期 UIView的生命周期 言叶之庭.jpeg 一. iOS程序的启动执行顺序 程序启动顺序图 iOS启动原理图.png 具体执行流程 程序入口进入main函数,设置AppDelegate称为函数的代理 程序完成加载[AppDelegate application:didFinishLaunchingWithOptions:] 创建window窗口 程序被激活[

iOS库--.a与.framework

一.什么是库? 库是共享程序代码的方式,一般分为静态库和动态库. 二.静态库与动态库的区别? 静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝. 动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存. 三.iOS里静态库形式? .a和.framework 四.iOS里动态库形式? .dylib和.framework 五.framework为什么既是静态库又是动态库? 系统的.framework是动态库,我们自己建立的.fram