NSURLSession

iOS URL系统概述:

The URL loading system includes classes that load URLs along with a number of important helper classes that work with those URL loading classes to modify their behavior. The major helper classes fall into five categories: protocol support, authentication and credentials, cookie storage, configuration management, and cache management.(URL加载系统包括URL加载类和一些辅助类,这些辅助类完善URL加载类的操作)

1.URL支持的协议:
The URL loading system provides support for accessing resources using the following protocols:ftp:,http:,https:,data:,file:
It also transparently supports both proxy servers and SOCKS gateways using the user’s system preferences.
(URL:统一资源定位符(uniform resource locator),是在网络中定位资源位置的表示方法.
组成:协议/服务器/路径/文件名;协议(schema)包括http,https,ftp,mail,file,telnet等)
URLloadsystem
2.URL加载类的区别

  • Using the NSURLSession class to asynchronously fetch the contents of a URL to memory or download files to disk in iOS 7 and later or OS X v10.9 and later
  • Using NSURLConnection to asynchronously fetch the contents of a URL to memory in older versions of iOS or OS X.
  • Using NSURLDownload to download files asynchronously to disk in older versions of OS X.

    NSURLSession

    1.NSURLSession Class

  • NSURLSession—A session object.
  • NSURLSessionConfiguration—A configuration object used when initializing the session.
  • NSURLSessionTask—The base class for tasks within a session.
  • : NSURLSessionDataTask—A task for retrieving the contents of a URL as an NSData object
  • :: NSURLSessionUploadTask—A task for uploading a file, then retrieving the contents of a URL as an NSData object
  • : NSURLSessionDownloadTask—A task for retrieving the contents of a URL as a temporary file on disk

    NSURLSession Protocol

  • NSURLSessionDelegate
  • :NSURLSessionTaskDelegate<NSURLSessionDelegate>
  • ::NSURLSessionDataDelegate<NSURLSessionTaskDelegate>
  • ::NSURLSessionDownloadDelegate<NSURLSessionTaskDelegate>

    2.Use NSURLSession

  • 1) Create a session configuration.

    1
    [NSURLSessionConfiguration defaultSessionConfiguration]
  • 2) Create a session, specifying a configuration object and, optionally, a delegate.

    1
    2
    3
    4
    sessionWithConfiguration:
    //根据创建的Configuration创建一个Session,系统默认创建一个新的OperationQueue处理Session的消息。
    sessionWithConfiguration:delegate:delegateQueue:
    //设定回调的delegate(这个delegate会被强引用),并设定delegate在哪个OperationQueue回调,如果设置为[NSOperationQueue mainQueue]就能在主线程进行回调非常的方便。
  • 3) Create task objects within a session that each represent a resource request

    1
    2
    3
    4
    5
    - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
    - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
    - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
    - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
    NSURLSessionDownloadTask *task = [NSURLSession downloadTaskWithURL:url]
  • 4) Call its delegate methods.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //NSURLSessionTaskDelegate 
    - (void)URLSession:task:didCompleteWithError:
    //NSURLSessionDataDelegate
    - (void)URLSession:dataTask:didReceiveResponse:completionHandler:
    - (void)URLSession:dataTask:didReceiveData:
    //NSURLSessionDownloadDelegate
    - (void)URLSession:downloadTask:didFinishDownloadingToURL:
    - (void)URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
    - (void)URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:

3.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
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
96
97
98
99
100
101
102
103
104
105
#import "ViewController.h"

@interface ViewController () <NSURLSessiondownloaddelegate,NSURLSessiondatadelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
- (IBAction)download:(UIButton *)sender;
@property (nonatomic, strong) NSURLSessionDownloadTask *task;
@property (nonatomic, strong) NSData *resumeData;
@property (nonatomic, strong) NSURLSession *session;
@end

@implementation ViewController
//懒加载
- (NSURLSession *)session
{
if (!_session) {
// 获得session
NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
self.session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
- (IBAction)download:(UIButton *)sender {
// 按钮状态取反
sender.selected = !sender.isSelected;
if (self.task == nil) {
// 开始(继续)下载
if (self.resumeData) {
// 恢复
[self resume];
} else {
// 开始
[self start];
}
} else
{
// 暂停
[self pause];
}
}
/**
* 从零开始
*/

- (void)start
{
// 1.创建一个下载任务
NSURL *url = [NSURL URLWithString:@"http://192.168.1.110:8080/Server/resources"];
self.task = [self.session downloadTaskWithURL:url];
// 2.开始任务
[self.task resume];
}
/**
* 恢复(继续)
*/

- (void)resume
{
// 传入上次暂停下载返回的数据,就可以恢复下载
self.task = [self.session downloadTaskWithResumeData:self.resumeData];

// 开始任务
[self.task resume];

// 清空
self.resumeData = nil;
}
/**
* 暂停
*/

- (void)pause
{
__weak typeof(self) weakSelf = self;//弱引用,避免block内强引用循环
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
// resumeData : 包含了继续下载的开始位置\下载的url
weakSelf.resumeData = resumeData;
weakSelf.task = nil;
}];
}
#pragma mark - NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// response.suggestedFilename : 建议使用的文件名,一般跟服务器端的文件名一致
NSString *file = [caches stringByAppendingPathComponent:downloadTask.response.suggestedFilename];

// 将临时文件剪切或者复制Caches文件夹
NSFileManager *mgr = [NSFileManager defaultManager];

// AtPath : 剪切前的文件路径
// ToPath : 剪切后的文件路径
[mgr moveItemAtPath:location.path toPath:file error:nil];
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
NSLog(@"获得下载进度--%@", [NSThread currentThread]);
// 获得下载进度
self.progressView.progress = (double)totalBytesWritten / totalBytesExpectedToWrite;
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
}

委托三步走

OC通过协议(protocol)可以实现多继承,协议最常用途是委托模式(Delegate pattern)。
委托模式是实现对象间通信,传递数据的方式之一,此模式可以将数据与业务逻辑解耦。
如显示一系列数据的视图,此视图应只包含显示数据所需的逻辑代码,不应决定要显示何种数据以及数据之间如何交互。
这些将通过协议来实现,利于解耦,这种模式就是委托模式。
一般将处理数据和处理事件的协议称为数据源(data source)和委托(delegate)。 

委托模式建立步骤:

1.建立委托协议。通常命名方式是委托对象类名+DataSource或者Delegate

delegate1

2.委托对象中包含委托协议类型的属性,使用该属性(即调用委托协议方法)。弱类型,避免强引用循环

delegate2

3.在遵循该委托协议的类中,1)设置委托对象的协议属性 2)实现协议方法

delegate3

初识Block

iOS三大回调方式:目标动作回调,委托回调,代码块回调。block最大的用处就是回调,可以代替委托。
Block:一段代码,相当于函数指针,指向一段代码的内存地址。可以访问外部作用域的变量,可用于回调。可以作为参数传递。

1.基础

1)定义
格式:(ReturnType) (^BlockName) (Args , Args2){}

1
2
3
4
int (^addBlock)(int a,int b) = ^(int a,int b){
return a + b;
};
addBlock(2,3);

定义一个名为addBlock的变量,需要两个参数。

1
2
3
void (^someBlock)() = ^{
//the block of code
};

Block无参数时,可以省略括号^{}.
2)创建块类型typedef

1
typedef  int  (^EOCSomeBlock) (BOOL flag, int value);

创建了一个名为EOCSomeBlock的类型

1
2
3
EOCSomeBlock   block =  ^(BOOL flag  , int  value ){
//some code
}

定义一个EOCSomeBlock类型的变量block
3)block 修饰可读写变量
block块内部可以获取在其声明范围的变量,但是只能读不能写。声明变量的时候加上
block修饰,就可以在块内修改该变量了。block代码分配在栈上,__block 将变量分配到堆上。

1
2
3
4
5
6
7
8
9
//简写,相当于[NSArray arrayWithObjects:[NSNumber numberWithInt:3],[NSNumber numberWithInt:5], nil];
NSArray *array = @[@3,@5,@6,@8,@13];
__block NSInteger count = 0;
//NSArray 枚举数组中每项
[array enumerateObjectsUsingBlock:^(NSNumber *number, NSUInteger idx,BOOL *stop){
if ([number compare:@2] == NSOrderedAscending) {
count++;
}
}];

4)weak 避免引用循环
将块定义在类的实例方法中,块内可以访问类的所有实例变量,还可以使用self变量。
在内存分配管理中,会有一个强指针指向block ,而block内部的self本身也有一个强指针指向block,就造成了强引用循环,双方都不能释放。
使用
weak创建弱引用变量,该变量指向的对象,当没有指针指向时,会设置为nil ,避免了引用环(retain cycle)

1
2
3
4
__weak MyClass *weakSelf = self;
[self.myBlocks addObject:^ {
[weakSelf doSomething];
}];

2.使用场景

1)枚举(数组,字典枚举每项),视图动画,排序
2)通知Notification
3)错误处理
4)多线程GCD(重要功能)
5)代替委托(delegate)功能

NSURLConnection

The NSURLConnection class provides convenience class methods to load URL requests both asynchronously using a callback block and synchronously.(NSURLConnection提供采用回调方式的异步请求和同步请求)

During a request, the connection maintains a strong reference to its delegate. It releases that strong reference when the connection finishes loading, fails, or is canceled.(在请求期间,NSURLConnection对它的委托保持强引用,当结束请求或者请求失败、被取消时才会释放对委托的引用)

一、NSURLConnection的常用类

(1)NSURL:请求地址

(2)NSURLRequest:封装一个请求,保存发给服务器的全部数据,包括一个NSURL对象,请求方法、请求头、请求体….

(3)NSMutableURLRequest:NSURLRequest的子类

(4)NSURLConnection:负责发送请求,建立客户端和服务器的连接。发送NSURLRequest的数据给服务器,并收集来自服务器的响应数据

二、NSURLConnection的使用

1.步骤

(1)设置请求路径.创建一个NSURL对象,设置请求路径(设置请求路径)

(2)创建请求对象.传入NSURL创建一个NSURLRequest对象,设置请求头和请求体(创建请求对象)

(3)发送请求.使用NSURLConnection发送NSURLRequest(发送请求)

2.同步请求与异步请求

(1)同步请求.服务器无返回数据会阻塞当前线程,有返回值

1
2
3
4
5
6
7
8
//1.设置请求路径
NSString *urlStr=[NSString stringWithFormat:@"http://192.168.0.101:8080/GWServer/login?username=%@&pwd=%@",self.username.text,self.pwd.text];
NSURL *url=[NSURL URLWithString:urlStr];
//2.创建请求对象
NSURLRequest *request=[NSURLRequest requestWithURL:url];
//3.发送同步请求,在主线程执行,iOS9.0已弃用 sendSynchronousRequest
NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSLog(@"--%d--",data.length);

(2)异步请求.无返回值,两种方式:使用block回调或者使用代理

使用block回调

1
2
3
4
5
6
7
8
9
10
//创建一个队列(默认添加到该队列中的任务异步执行)
//NSOperationQueue *queue=[[NSOperationQueue alloc]init];
//获取一个主队列
NSOperationQueue *queue=[NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(@"--block回调数据--%@---%d", [NSThread currentThread],data.length);
//解析data
......
NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSLog(@"%@",dict);

代理

  • NSURLConnectionDelegate调用代理方法 connection:didFailWithError:
  • NSURLConnectionDataDelegate调用代理方法connection:didReceiveResponse:,connection:didReceiveData:,connectionDidFinishLoading:
  • NSURLConnectionDownloadDelegate
    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
    //设置请求路径
    NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.101:8080/GWServer/login?username=%@&pwd=%@",self.username.text,self.pwd.text];
    NSURL *url=[NSURL URLWithString:urlStr];
    //创建请求对象
    // NSURLRequest *request=[NSURLRequest requestWithURL:url];//默认就是GET请求
    //设置请求超时
    NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:url];
    request.timeoutInterval=5.0;
    //发送请求
    NSURLConnection *conn=[NSURLConnection connectionWithRequest:request delegate:self];
    #pragma mark- NSURLConnectionDataDelegate代理方法
    //接收到服务器的响应
    -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
    {
    //初始化数据
    self.responseData=[NSMutableData data];
    }
    //接收到服务器的数据
    -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
    //拼接数据
    [self.responseData appendData:data];
    NSLog(@"%d---%@--",self.responseData.length,[NSThread currentThread]);
    }
    //服务器的数据加载完毕
    -(void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
    //处理服务器返回的所有数据
    NSDictionary *dict=[NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:nil];
    NSLog(@"%d---%@--",self.responseData.length,[NSThread currentThread]);
    }
    //请求失败
    -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
    NSLog(@"请求错误");
    }