网络请求 - NSURLSession

在WWDC 2013中,Apple的团队对NSURLConnection进行了重构,并推出了NSURLSession作为替代。

NSURLSession也是一组相互依赖的类,它的大部分组件和NSURLConnection中的组件相同如NSURLRequest,NSURLCache等。而NSURLSession的不同之处在于,它将NSURLConnection替换为NSURLSession和NSURLSessionConfiguration,以及3个NSURLSessionTask的子类:NSURLSessionDataTask, NSURLSessionUploadTask, 和NSURLSessionDownloadTask。

URL加载框架

NSRULSession 网络请求系统包括一个session、一个configuration、一个Task已经Task附带的delegate。一个NSURLSessionConfiguration,总共有三种模式。一个NSURLSessionTask。NSURLSessionTask是抽闲类,对应的代理NSURLSessionTaskDelegate。我们具体使用的时候,会使用他的三种子类,而且每个子类都有对应的delegate。

初始化方法:

+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;

属性:
delegate: 创建 session 时传入的 delegate,session 对delegate 强引用,只有当 session 执行 invalidate 相关方法后,才会释放引用

delegateQueue: 创建 session 时传入的 delegateQueue,所有的代理方法和 block 回调都会在这个 queue 中执行。

sessionDescription: 可给 session 设置一个描述,用于调试或界面显示。

@property (readonly, retain) NSOperationQueue *delegateQueue;
@property (nullable, readonly, retain) id <NSURLSessionDelegate> delegate;
@property (nullable, copy) NSString *sessionDescription;

创建 task:

可参考NSURLSession

NSURLSession创建Task.png

block形式创建并执行block,和delegate一样,Block也有dataTask系列、downloadTask系列、uploadTask系列

block创建Task.png

获取 session 中的 task:

- (void)getTasksWithCompletionHandler:(void (^)(NSArray<NSURLSessionDataTask *> *dataTasks, NSArray<NSURLSessionUploadTask *> *uploadTasks, NSArray<NSURLSessionDownloadTask *> *downloadTasks))completionHandler;
- (void)getAllTasksWithCompletionHandler:(void (^)(NSArray<__kindof NSURLSessionTask *> *tasks))completionHandler;

设置代理之后的强引用问题

NSURLSession 对象在使用的时候,如果设置了代理,那么 session 会对代理对象保持一个强引用,在合适的时候应该主动进行释放
可以在控制器调用 viewDidDisappear 方法的时候来进行处理,可以通过调用 invalidateAndCancel 方法或者是 finishTasksAndInvalidate 方法来释放对代理对象的强引用.

invalidateAndCancel 方法直接取消请求然后释放代理对象,
finishTasksAndInvalidate 方法等请求完成之后释放代理对象

- (void)finishTasksAndInvalidate;
- (void)invalidateAndCancel;

一、NSURLSessionConfiguration类

里面所有属性的设置都是对当前NSURLSession的整体设置。NSURLSessionConfiguration会话配置

默认会话模式(default):工作模式类似于原来的NSURLConnection,使用的是基于磁盘缓存的持久化策略,使用用户keychain中保存的证书进行认证授权。

瞬时会话模式(ephemeral):该模式不使用磁盘保存任何数据。所有和会话相关的caches,证书,cookies等都被保存在RAM中,因此当程序使会话无效,这些缓存的数据就会被自动清空。

后台会话模式(background):该模式在后台完成上传和下载,在创建Configuration对象的时候需要提供一个NSString类型的ID用于标识完成工作的后台会话。

+ (NSURLSessionConfiguration *)defaultSessionConfiguration; 

+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration; 
 
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
//方法中的identifier参数指定了会话的ID,用于标记后台的session。

timeoutIntervalForRequest: 指定了请求以及该资源的超时时间间隔, 当新数据到达时,与该值相关联的计时器将被重置。默认60s
timeoutIntervalForResource: 这应该只用于后台传输,默认7天

@property NSTimeInterval timeoutIntervalForRequest;

@property NSTimeInterval timeoutIntervalForResource;

networkServiceType对标准的网络流量,网络电话,语音,视频,以及由一个后台进程使用的流量进行了区分。设置后便于系统对网络请求的区分,系统能根据提供的信息来优化网络处理,从而优化电池寿命,网络性能等等

@property NSURLRequestNetworkServiceType networkServiceType;

allowsCellularAccess: 属性指定是否允许使用蜂窝连接;
discretionary: 属性为YES时表示当程序在background运作时由系统自己选择最佳的网络连接配置,该属性可以节省通过蜂窝连接的带宽。在使用后台传输数据的时候,建议使用discretionary属性,而不是allowsCellularAccess属性,因为它会把WiFi和电源可用性考虑在内。

@property BOOL allowsCellularAccess;  
 
@property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);

此属性根据此配置确定会话中的任务对每个主机的最大并发连接数。MacOS中的默认值为6,iOS中为4.如果有多个会话,会超过这个限制

@property NSInteger HTTPMaximumConnectionsPerHost;

HTTPShouldUsePipelining表示receiver(理解为iOS客户端)的下一个信息是否必须等到上一个请求回复才能发送。
如果为YES表示可以,NO表示必须等receiver收到先前的回复才能发送下个信息。默认为NO

@property BOOL HTTPShouldUsePipelining;

Cookies是一种能够让网站服务器把少量数据储存到客户端的硬盘或内存,或是从客户端的硬盘读取数据的一种技术。Cookies是当你浏览某网站时,由Web服务器置于你硬盘上的一个非常小的文本文件,它可以记录你的用户ID、密码、浏览过的网页、停留的时间等信息。当你再次来到该网站时,网站通过读取Cookies,得知你的相关信息,就可以做出相应的动作,如在页面显示欢迎你的标语,或者让你不用输入ID、密码就直接登录等等。默认为YES

@property BOOL HTTPShouldSetCookies;

//Cookie数据的接收协议(接收所有,不接收,默认只接受主文档)
@property NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;

//Cookie 处理类 [详解](http://www.jb51.net/article/88173.htm)
@property (nullable, retain) NSHTTPCookieStorage *HTTPCookieStorage;

是会话使用的证书存储。默认情况下,NSURLCredentialStorage 的+ sharedCredentialStorage 会被使用使用

@property (nullable, retain) NSURLCredentialStorage *URLCredentialStorage;

NSURLCache详解和使用

@property (nullable, retain) NSURLCache *URLCache;

指定了一组默认的可以设置出站请求的数据头

@property (nullable, copy) NSDictionary *HTTPAdditionalHeaders;

NSHTTPCookieStorage

NSURLRequest都会帮你主动记录下来你访问的站点设置的 cookie,而且很负责任的,当你下次再访问这个站点时,NSURLRequest会拿着上次保存下来了的cookie继续去请求。

NSHttpCookiesStorage是一个单例,管理所有的Cookie,每个Cookie都是一个NSHTTPCookie的实例,所有应用的cookies都被保存在这个NSHTTPCookieStorage的单例中,并且跨进程同步。 但为了安全,每个应用都有自己的沙盒,A应用的cookie是不能被B应用访问的。

查看cookie

NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
   NSLog(@"%@", cookie);
}

清空cookie

NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  NSArray *_tmpArray = [NSArray arrayWithArray:[cookieJar cookies]];
  for (id obj in _tmpArray) {
    [cookieJar deleteCookie:obj];
  }

指定cookie

NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
  [cookieProperties setObject:@"username" forKey:NSHTTPCookieName];
  [cookieProperties setObject:@"rainbird" forKey:NSHTTPCookieValue];
  [cookieProperties setObject:@"cnrainbird.com" forKey:NSHTTPCookieDomain];
  [cookieProperties setObject:@"cnrainbird.com" forKey:NSHTTPCookieOriginURL];
  [cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
  [cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];
  NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
  [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];

NSURLCache

NSURLCache详解和使用

在应用程序的开发中,为了减小对网络的依赖,提高程序性能,常常会对一些非实时性的数据进行缓存处理,NSURLCache类用于管理NSURLRequest请求缓存。

缓存的实现说明:由于GET请求一般用来查询数据,POST请求一般是发大量数据给服务器处理(变动性比较大),因此一般只对GET请求进行缓存,而不对POST请求进行缓存。

SURLCache的常见用法:

获得全局缓存对象(没必要手动创建)

NSURLCache *cache = [NSURLCache sharedURLCache]; 

设置内存缓存的最大容量(字节为单位,默认为512KB)
设置硬盘缓存的最大容量 (字节为单位,默认为10M)

@property NSUInteger memoryCapacity;
@property NSUInteger diskCapacity;
@property (readonly) NSUInteger currentMemoryUsage;

取得某个请求的缓存,存储缓存

- (nullable NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request;
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request;

清除缓存

- (void)removeCachedResponseForRequest:(NSURLRequest *)request;
- (void)removeAllCachedResponses;
- (void)removeCachedResponsesSinceDate:(NSDate *)date NS_AVAILABLE(10_10, 8_0);

二、NSURLSessionTask类

可以参考这篇文章AFNetWorking源码之NSURLSession系列概述

从上面我们发现Task和delegate有一套对应的继承关系:

对应的继承关系.png

从继承关系上,我们就可以理解在初始化的时候,只通过设置NSURLSession对象的delegate就可以了。因为根据不同的task,其实就是设置了不同的delegate。这个设计避免了多次设置delegate的情况,同时也根据不同的task实现不同的delegate方法。

NSURLSessionTask的属性和方法

@property (readonly)                 NSUInteger    taskIdentifier;
@property (nullable, readonly, copy) NSURLRequest  *originalRequest;
@property (nullable, readonly, copy) NSURLRequest  *currentRequest;
@property (nullable, readonly, copy) NSURLResponse *response;

//task优先级
@property float priority

//当前接收量
@property (readonly) int64_t countOfBytesReceived;

//当前发送量
@property (readonly) int64_t countOfBytesSent;

//发送数据总数
@property (readonly) int64_t countOfBytesExpectedToSend;

//接收数据总数,来源于响应头Content-Length字段
@property (readonly) int64_t countOfBytesExpectedToReceive;

//cancel方法可以取消当前的任务,你也可以向处于suspend状态的任务发送cancel消息,任务如果被取消便不能再恢复到之前的状态.
- (void)cancel;

//suspend可以让当前的任务暂停
- (void)suspend;

//resume方法不仅可以启动任务,还可以唤醒suspend状态的任务
- (void)resume;

1、代理说明

NSURLSessionDelegate

当一个session遇到系统错误或者未检测到的错误的时候,就会调用这个方法。

- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error

当请求需要认证、或者https证书认证的时候,我们就需要在这个方法里面处理。

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler

如果应用进入后台、这个方法会被调用。我们在这里可以对session发起的请求做各种操作比如请求完成的回调等。

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session 

NSURLSessionTaskDelegate

当请求重定向的时候调用这个方法。我们必须设置一个新的NSURLRequest对象传入completionHandler来重定向新的请求,但是当session是background模式的时候,这个方法不会被调用。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
        newRequest:(NSURLRequest *)request
 completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler{

}

当请求需要认证的时候调用这个方法。如果没有实现这个代理,那么请求认证这个过程不会被调用。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{

}

如果请求需要一个新的请求体时,这个方法就会被调用。比如认证失败的时候,我们可以通过这个机会从新认证。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
 needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler
 

当我们上传数据的时候,我们可以通过这个代理方法获取上传进度。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
   didSendBodyData:(int64_t)bytesSent
    totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend

当task的统计信息收集好了以后,调用这个方法。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics 

当一个task出错的时候,会调用这个方法。如果error是nil,也会调用这个方法,表示task完成。

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error{
    NSLog(@"数据返回以后,不管有错没错都回调用,如果没错,error及时nil");
}

NSURLSessionDataDelegate

当一个task接收到返回信息。当所有信息都接收完毕以后,completionHandler会被调用。我们可以在这里取消一个网络请求或者把一个datatask转换为downloadtask。如果没有实现这个代理方法,我们也可以通过task的response属性获取到对应的数据。background模式的uploadtask不会调用这个方法。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
 

当一个datatask转换为一个downloadtask以后会调用。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask{
    // 允许处理服务器的响应,才会继续接收服务器返回的数据
    completionHandler(NSURLSessionResponseAllow);
}

当data可以使用的时候,调用这个方法。我们可以在这里获取data。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data

允许我们在这里调用completionHandler缓存data,或者传入nil来禁止缓存

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
 willCacheResponse:(NSCachedURLResponse *)proposedResponse
 completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler{

}

NSURLSessionDownloadDelegate

当一个下载task任务完成以后,这个方法会被调用。我们可以在这里移动或者复制download的数据

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location

获取下载进度

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite

断点下载。如果下载出错,NSURLSessionDownloadTaskResumeData里面包含重新开始下载的数据。

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes

2、NSURLSession子类

NSURLSessionDataTask

GET请求:

// 快捷方式获得session对象
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/login?username=daka&pwd=123"];
// 通过URL初始化task,在block内部可以直接对返回的数据进行处理
NSURLSessionTask *task = [session dataTaskWithURL:url
                               completionHandler:^(NSData *data, NSURLResponse *response, NSError error) {
    NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
}];

// 启动任务
[task resume]

POST请求:

NSURL *url = [NSURL URLWithString:@"http://www.daka.com/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=daka&pwd=123" dataUsingEncoding:NSUTF8StringEncoding];

NSURLSession *session = [NSURLSession sharedSession];
// 由于要先对request先行处理,我们通过request初始化task
NSURLSessionTask *task = [session dataTaskWithRequest:request
                                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]); }];
[task resume];

NSURLSessionDataDelegate代理方法:

// 使用代理方法需要设置代理,但是session的delegate属性是只读的,要想设置代理只能通过这种方式创建session
//delegateQueue参数表示协议方法将会在哪个队列(NSOperationQueue)里面执行.
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
                                                      delegate:self
                                                 delegateQueue:[[NSOperationQueue alloc] init]];

// 创建任务(因为要使用代理方法,就不需要block方式的初始化了)
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.daka.com/login?userName=daka&pwd=123"]]];

// 启动任务
[task resume];

//对应的代理方法如下:

// 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 {
    // 处理每次接收的数据
}

// 3.请求成功或者失败(如果失败,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    // 请求完成,成功或者失败的处理
}

NSURLSessionDownloadTask

NSURLSessionDownloadTask详解

普通下载:

NSURLSessionDownloadTask同样提供了通过NSURL和NSURLRequest两种方式来初始化并通过block进行回调的方法.下面以NSURL初始化:

NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http://www.daka.com/resources/image/icon.png"] ;
NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
    // location是沙盒中tmp文件夹下的一个临时url,文件下载后会存到这个位置,由于tmp中的文件随时可能被删除,所以我们需要自己需要把下载的文件挪到需要的地方
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
    // 剪切文件
    [[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:path] error:nil];
}];
    // 启动任务
    [task resume];

downloadTask也提供了配套的代理方法:

// NSURLSessionConfiguration 方式创建 NSURLSession ,调用代理方法
NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];  
    self.currentSession = [NSURLSession sessionWithConfiguration:defaultConfig delegate:self delegateQueue:nil];  
    self.currentSession.sessionDescription = kCurrentSession;
    [self.cancellableTask resume];


// 每次写入调用(会调用多次)  实现下载进度的显示
- (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];
}

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

}

断点下载:

NSURLSession的这种断点下载只支持应用内断点,如果程序在下载过程中途关闭,则不能恢复下载.

// 使用这种方式取消下载可以得到将来用来恢复的数据,保存起来
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
    self.resumeData = resumeData;
}];

// 由于下载失败导致的下载中断会进入此协议方法,也可以得到用来恢复的数据
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    // 保存恢复数据
    self.resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
}

// 恢复下载时接过保存的恢复数据
self.task = [self.session downloadTaskWithResumeData:self.resumeData];
// 启动任务
[self.task resume];


/* 另外在恢复下载时,NSURLSessionDownloadDelegate中的以下方法将被调用,从fileOffset位移处恢复下载任务 */  
- (void)URLSession:(NSURLSession *)session  
      downloadTask:(NSURLSessionDownloadTask *)downloadTask  
 didResumeAtOffset:(int64_t)fileOffset  
expectedTotalBytes:(int64_t)expectedTotalBytes {  
    NSLog(@"NSURLSessionDownloadDelegate: Resume download at %lld", fileOffset);  
}

后台下载:

首先创建一个后台session单例,这里的Session配置使用后台配置模式,使用backgroundSessinConfiguration:方法配置时应该通过后面的参数为该后台进程指定一个标识符,在有多个后台下载任务时这个标识符就起作用了。

/* 1、创建一个后台session单例 */  
- (NSURLSession *)backgroundSession {  
    static NSURLSession *backgroundSess = nil;  
    static dispatch_once_t onceToken;  
    dispatch_once(&onceToken, ^{  
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:kBackgroundSessionID];  
        backgroundSess = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];  
        backgroundSess.sessionDescription = kBackgroundSession;  
    });  
      
    return backgroundSess;  
}


/* 2、在创建后台下载任务时,应该使用后台session创建,然后resume。*/ 

- (IBAction)backgroundDownload:(id)sender {  
    NSString *imageURLStr = @"http://farm3.staticflickr.com/2831/9823890176_82b4165653_b_d.jpg";  
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURLStr]];  
    self.backgroundTask = [self.backgroundSession downloadTaskWithRequest:request];  
    [self setDownloadButtonsWithEnabled:NO];  
    self.downloadedImageView.image = nil;  
    [self.backgroundTask resume];  
}

/*3  在程序进入后台后,如果下载任务完成,程序委托中的对应方法将被回调:*/
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {  
    NSLog(@"Application Delegate: Background download task finished");  
      
    // 设置回调的完成代码块  
    self.backgroundURLSessionCompletionHandler = completionHandler;  
}

/*4、然后调用NSURLSessionDownloadDelegate中的方法:(该方法只有下载成功才被调用)*/ 
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL*)location{
else if (session == self.backgroundSession) {  
    self.backgroundTask = nil;  
    AppDelegate *appDelegate = [AppDelegate sharedDelegate];  
    if (appDelegate.backgroundURLSessionCompletionHandler) {  
        // 执行回调代码块  
        void (^handler)() = appDelegate.backgroundURLSessionCompletionHandler;  
        appDelegate.backgroundURLSessionCompletionHandler = nil;  
        handler();  
    }  
}

 
/*5、 完成下载任务,无论下载成功还是失败都调用该方法 */  
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {  
    NSLog(@"NSURLSessionDownloadDelegate: Complete task");  
      
    dispatch_async(dispatch_get_main_queue(), ^{  
        [self setDownloadButtonsWithEnabled:YES];  
    });  
      
    if (error) {  
        NSLog(@"下载失败:%@", error);  
        [self setDownloadProgress:0.0];  
        self.downloadedImageView.image = nil;  
    }  
}

NSURLSessionUploadTask

在NSURLSession中,文件上传方式主要有以下两种:

NSURLSessionUploadTask *task =
[[NSURLSession sharedSession] uploadTaskWithRequest:request
                                           fromFile:fileName
                                  completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
}];


//我们会使用POST方式进行文件上传,所以较多使用第二种方式
[self.session uploadTaskWithRequest:request
                            fromData:body
                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
 NSLog(@"-------%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
 }];


三、NSURLRequest

初始化方法:

//通过这种方式创建的请求对象 默认使用NSURLRequestUseProtocolCachePolicy缓存逻辑 默认请求超时时限为60s
+ (instancetype)requestWithURL:(NSURL *)URL;

+ (instancetype)requestWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;

//init方法进行对象的创建 默认使用NSURLRequestUseProtocolCachePolicy缓存逻辑 默认请求超时时限为60s
- (instancetype)initWithURL:(NSURL *)URL;

- (instancetype)initWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval NS_DESIGNATED_INITIALIZER;

常用属性:

只读属性 获取请求的超时时限

@property (readonly) NSTimeInterval timeoutInterval;

主文档地址 这个地址用来存放缓存

@property (nullable, readonly, copy) NSURL *mainDocumentURL;

获取网络请求的服务类型

获取是否允许使用服务商蜂窝网络

@property (readonly) NSURLRequestNetworkServiceType networkServiceType
@property (readonly) BOOL allowsCellularAccess
@property (nullable, readonly, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;

NSMutableURLRequest

继承于NSURLRequest

常用属性:

//设置请求的URL
@property (nullable, copy) NSURL *URL;
//设置请求的缓存策略
@property NSURLRequestCachePolicy cachePolicy;
//设置超时时间
@property NSTimeInterval timeoutInterval;
//设置缓存目录
@property (nullable, copy) NSURL *mainDocumentURL;
//设置网络服务类型
@property NSURLRequestNetworkServiceType networkServiceType NS_AVAILABLE(10_7, 4_0);
//设置是否允许使用服务商蜂窝网
@property BOOL allowsCellularAccess NS_AVAILABLE(10_8, 6_0);

以下属性的设置必须使用NSMutableURLRequest类,HTTP/HTTPS协议相关请求的属性设置,如果是NSURLRequest,则只可以读,不可以修改。

//设置HPPT请求方式 默认为“GET”
@property (copy) NSString *HTTPMethod;
//通过字典设置HTTP请求头的键值数据
@property (nullable, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
//设置http请求头中的字段值
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(NSString *)field;
//向http请求头中添加一个字段
- (void)addValue:(NSString *)value forHTTPHeaderField:(NSString *)field;
//设置http请求体 用于POST请求
@property (nullable, copy) NSData *HTTPBody;
//设置http请求体的输入流
@property (nullable, retain) NSInputStream *HTTPBodyStream;
//设置发送请求时是否发送cookie数据
@property BOOL HTTPShouldHandleCookies;
//管道线技术,设置请求时是否按顺序收发 默认禁用 在某些服务器中设为YES可以提高网络性能
@property BOOL HTTPShouldUsePipelining;

四、NSURLResponse

初始化方法不经常用到,略。

常用的属性:

//服务的URL地址
@property (nullable, readonly, copy) NSURL *URL;
//返回的数据信息(纯文本,视频,语音,超文本等)
@property (nullable, readonly, copy) NSString *MIMEType;
//将要返回的数据的大小
@property (readonly) long long expectedContentLength;
//编码的名称
@property (nullable, readonly, copy) NSString *textEncodingName;
//获取服务器的文件的名称
@property (nullable, readonly, copy) NSString *suggestedFilename;

NSHTTPURLResponse

继承于NSURLResponse

SInteger statusCode:返回的状态码,成功,失败,无效请求等,(200,404,400等)
NSDictionary* allHeaderFields:返回数据的头部信息,key-value格式

@property (readonly) NSInteger statusCode;
@property (readonly, copy) NSDictionary *allHeaderFields;


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容