apple官方文档翻译:使用NSURLSession(二)

简单的代理类的接口

下面的代码片段基于清单1-1所示接口

清单1-1

#import <Foundation/Foundation.h>

typedef void (^CompletionHandlerType)();

@interface MySessionDelegate : NSObject     <NSURLSessionDelegate, NSURLSessionTaskDelegate,    NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>
@property NSURLSession *backgroundSession;
@property NSURLSession *defaultSession;
@property NSURLSession *ephemeralSession;

#if TARGET_OS_IPHONE
@property NSMutableDictionary       *completionHandlerDictionary;
#endif

- (void) addCompletionHandler: (CompletionHandlerType) handler forSession: (NSString *)identifier;
- (void) callCompletionHandlerForSession: (NSString *)identifier;
@end

创建并配置一个会话

NSURLSession提供了大量的配置选项:

  • 支持对缓存,cookies,证书的私有存储,以及对单例会话的特定协议
  • 关联到一个特定请求(任务),或者一组请求(会话)的认证
  • 通过URL上传或下载文件,支持将元数据分割成基于文件内容的短数据
  • 配置每个主机的最大连接数
  • 当资源无法在一个确定时间内下载时,配置一个超时时间
  • 支持安全传输层协议(TLS)的版本区间
  • 自定义代理
  • cookie的管理策略
  • HTTP传输管理

大部分的配置都在一个configuration对象中设置,可以通用一些基本设置.初始化一个会话对象(session object)需要指定如下信息:

  • 一个configuration对象用来管理会话或任务的行为
  • 可选的,一个代理对象用来表示接收数据的进度,会话任务或会话其他事件的进度,比如服务器认证,决定一个加载请求是否可转换为下载请求,等等

如果没有指定一个代理,NSURLSession对象将使用系统提供得代理.在这种方式中,你可以轻松的使用NSURLSession替代已存在的sendAsynchronousRequest:queue:completionHandler:方法.

注意:如果app需要在后台进行数据传输,必须使用自定义代理.

创建一个会话对象之后,不能再去修改它的configuration对象和代理,除了重新创建一个会话.

清单1-2展示了创建默认会话,临时会话和后台会话的示例代码

#if TARGET_OS_IPHONE
self.completionHandlerDictionary = [NSMutableDictionary dictionaryWithCapacity:0];
#endif

/* Create some configuration objects. */

NSURLSessionConfiguration *backgroundConfigObject = [NSURLSessionConfiguration backgroundSessionConfiguration: @"myBackgroundSessionIdentifier"];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSessionConfiguration *ephemeralConfigObject = [NSURLSessionConfiguration ephemeralSessionConfiguration];

/* Configure caching behavior for the default session.
   Note that iOS requires the cache path to be a path relative
   to the ~/Library/Caches directory, but OS X expects an
   absolute path.
 */
#if TARGET_OS_IPHONE
NSString *cachePath = @"/MyCacheDirectory";

NSArray *myPathList = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *myPath    = [myPathList  objectAtIndex:0];

NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];

NSString *fullCachePath = [[myPath stringByAppendingPathComponent:bundleIdentifier] stringByAppendingPathComponent:cachePath];
NSLog(@"Cache path: %@\n", fullCachePath);
#else
NSString *cachePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"/nsurlsessiondemo.cache"];

NSLog(@"Cache path: %@\n", cachePath);
#endif

NSURLCache *myCache = [[NSURLCache alloc] initWithMemoryCapacity: 16384 diskCapacity: 268435456 diskPath: cachePath];
defaultConfigObject.URLCache = myCache;
defaultConfigObject.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;

/* Create a session for each configurations. */
self.defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
self.backgroundSession = [NSURLSession sessionWithConfiguration: backgroundConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
self.ephemeralSession = [NSURLSession sessionWithConfiguration: ephemeralConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];

除了后台配置对象(background configurations),可以重用配置对象来创建其他的会话.(不能重用后台配置对象是因为两个后台会话不能使用相同的标识符identifier)

你可以在任何时间安全的修改一个configuration对象.因为当创建一个会话时,configuration对象的传递是由深拷贝实现的,所以修改只会影响之后创建的会话,不会对已存在的会话造成影响.例如,你可能想创建另一个只有在WiFi环境下才能重连数据的会话,如1-3中所示:

清单1-3重用configuration对象

ephemeralConfigObject.allowsCellularAccess = YES;

// ...

NSURLSession *ephemeralSessionWiFiOnly =    [NSURLSession sessionWithConfiguration: ephemeralConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];

使用系统提供的代理抓取资源

最简单直接的使用NSURLSession的方法是用来替换掉之前的sendAsynchronousRequest:queue:completionHandler:方法.要这么做,你需要在app中实现两处代码:

  • 创建configuration对象,以及一个基于该configuration对象的会话对象
  • 一个完成处理程序来处理数据接收完成后要做的事情

使用系统提供的代理,你可以每个请求只用一行代码来抓取特定URL.清单1-4示例了最简单的实现.

注意:系统提供的代理仅仅实现了有限网络功能.如果app的需求超出了基本的URL加载,比如自定义认证或者数据后台下载,那么需要实现一个完整的代理,参见URL Session的生命周期.

清单1-4使用系统提供的代理请求资源:

NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];

[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString: @"http://www.example.com/"]
                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
           NSLog(@"Got response %@ with error %@.\n", response, error);
           NSLog(@"DATA:\n%@\nEND DATA\n",
                             [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding]);
                   }] resume];

使用自定义代理抓取数据

使用自定义代理检索数据必须实现下列方法:

  • URLSession:dataTask:didReceiveData:提供了任务请求返回的数据,周期性的返回数据块
  • URLSession:task:didCompleteWithError:表明数据是否全部完成接收

如果app需要在URLSession:dataTask:didReceiveData:方法返回之后使用数据,必须用代码实现数据存储.

例如,一个web浏览器可能需要根据之前接收的数据来渲染当前接收的数据.要实现这个功能可以使用一个NSMutableData对象来存储结果数据,然后使用 appendData: 来将当前接收的数据拼接到之前接收到的数据中.

清单1-5示例了如何创建开始一个数据任务:

NSURL *url = [NSURL URLWithString: @"http://www.example.com/"];

NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithURL: url];
[dataTask resume];

下载文件

某种程序上,下载文件和接收数据类似.app应当实现以下的代理方法:

  • URLSession:downloadTask:didFinishDownloadingToURL:提供app下载内容的临时存储目录. 注意:在这个方法返回之前,必须打开文件来进行读取或者将下载内容移动到一个永久目录.当方法返回后,临时文件将会被删除.
  • URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: 提供了下载进度的状态信息.
  • URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes: 告诉app尝试恢复之前失败的下载.
  • URLSession:task:didCompleteWithError:告诉app下载失败

如果将下载任务安排在后台会话中,在app非运行期间下载行为仍将继续.如果将下载任务安排在系统默认会话或者临时会话中,当app重新启动时,下载也将重新开始.

在跟服务器传输数据期间,如果用户进行了暂停操作,app可以调用cancelByProducingResumeData: 方法取消任务.然后,app可以将已传输的数据作为参数传递给downloadTaskWithResumeData:或者downloadTaskWithResumeData:completionHandler:来创建一个新的下载任务继续下载.

清单1-6示例了一个大文件的下载.清单1-7示例了下载任务的代理方法.

清单1-6 下载任务示例:

    NSURL *url = [NSURL URLWithString: @"https://developer.apple.com/library/ios/documentation/Cocoa/Reference/"
              "Foundation/ObjC_classic/FoundationObjC.pdf"];

NSURLSessionDownloadTask *downloadTask = [self.backgroundSession downloadTaskWithURL: url];
[downloadTask resume];

清单1-7 下载任务的代理方法:

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
      NSLog(@"Session %@ download task %@ finished downloading to URL %@\n",session, downloadTask, location);

#if 0
/* Workaround */
[self callCompletionHandlerForSession:session.configuration.identifier];
#endif

#define READ_THE_FILE 0
#if READ_THE_FILE
/* Open the newly downloaded file for reading. */
NSError *err = nil;
NSFileHandle *fh = [NSFileHandle fileHandleForReadingFromURL:location
    error: &err];

/* Store this file handle somewhere, and read data from it. */
// ...

#else
NSError *err = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *cacheDir = [[NSHomeDirectory()
    stringByAppendingPathComponent:@"Library"]
    stringByAppendingPathComponent:@"Caches"];
NSURL *cacheDirURL = [NSURL fileURLWithPath:cacheDir];
if ([fileManager moveItemAtURL:location
    toURL:cacheDirURL
    error: &err]) {

    /* Store some reference to the new URL */
} else {
    /* Handle the error. */
}
#endif

}

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    NSLog(@"Session %@ download task %@ wrote an additional %lld bytes (total %lld bytes) out of an expected %lld bytes.\n",
    session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
    NSLog(@"Session %@ download task %@ resumed at offset %lld bytes out of an expected %lld bytes.\n",
    session, downloadTask, fileOffset, expectedTotalBytes);
}

上传数据内容

app能通过三种方式通过提供HTTP POST请求体:NSData对象,文件和流.通常情况下,app应当:

  • 使用一个NSData对象,如果内存中已经存在相应数据,并且数据不会被无理由的销毁.
  • 使用文件形式,如果要上传的内容是通过文件形式存储在硬盘中的,或者将要上传的内容写入文件,要是这样可以解决内存的话.
  • 使用流,如果是从网络接收数据,或者转化已存在的提供了流的NSURLConnection代码.

无论你选择了哪种方法,如果app提供了自定义代理,都应该实现URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: 方法来获取上传进度.

此外,如果app使用流作为请求体,还必须提供一个自定义会话代理实现URLSession:task:needNewBodyStream:方法,详细描述在通过流上传数据

使用NSData对象上传

使用NSData对象上传数据,app需要调用uploadTaskWithRequest:fromData:或者uploadTaskWithRequest:fromData:completionHandler:来创建一个上传任务,将要上传的NSData对象传递给fromData参数.

会话对象根据NSData对象计算内容长度,赋值给请求头的Content-Length.app还要在URL request对象中提供服务器可能需要的请求头信息-例如:content type.

使用文件形式上传

使用文件形式上传,app需要调用 uploadTaskWithRequest:fromFile:或者uploadTaskWithRequest:fromFile:completionHandler: 方法来创建一个上传任务,以及一个文件路径来读取内容.

会话对象自动计算Content-Length,如果app没有提供 Content-Type,会话对象将自动生成一个.app还要在URL request对象中提供服务器可能需要的请求头信息

使用流形式上传

使用流来上传信息,app需要调用 uploadTaskWithStreamedRequest: 方法来创建一个上传任务.app提供一个绑定了流的request对象.app还要在URL request对象中提供服务器可能需要的请求头信息,比如content-type和content-length.

此外,因为会话对象不能保证必定能从提供的流中读取数据,所以app需要提供一个新的流以便会话重新进行请求(比如,认证失败).app需要实现 URLSession:task:needNewBodyStream:方法.当这个方法被调用时,app需要取得或者创建一个新的流,然后调用提供的完成处理块.

注意:因为app必须实现URLSession:task:needNewBodyStream:方法,所以这种形式不支持使用系统默认的代理.

使用下载任务来上传文件

当下载任务创建时,app需要提供一个NSData对象或者一个流作为NSURLRequest对象的参数.

如果使用数据流,app需要实现 URLSession:task:needNewBodyStream: 方法来处理认证失败的情况.详细描述在通过流上传数据

处理认证和安全传输确认

如果远程服务器返回一个状态值表明需要进行认证或者认证需要特定的环境(例如一个SSL客户端证书),NSURLSession调用会调用一个认证相关的代理方法.

  • 会话级别:NSURLAuthenticationMethodNTLM, NSURLAuthenticationMethodNegotiate, NSURLAuthenticationMethodClientCertificate,或者 NSURLAuthenticationMethodServerTrust,会话对象调用会话代理方法URLSession:didReceiveChallenge:completionHandler: .如果app没有提供会话代理,会话对象调用任务得代理方法URLSession:task:didReceiveChallenge:completionHandler:.
  • 非会话级别:NSURLSession对象调用会话代理方法URLSession:task:didReceiveChallenge:completionHandler:.如果app提供了会话代理,而且app需要处理认证,那么你必须在任务级别进行处理. 在非会话级别上,URLSession:didReceiveChallenge:completionHandler:不会被调用.

更多信息参见Authentication Challenges and TLS Chain Validation.

处理iOS后台活动

在iOS中使用NSURLSession,当一个下载任务完成时,app将会自动重启.app代理方法application:handleEventsForBackgroundURLSession:completionHandler: 负责重建合适的会话,存储完成处理器,并在会话对象调用会话代理的URLSessionDidFinishEventsForBackgroundURLSession: 方法时调用完成处理器.

清单1-8,清单1-9分别示例了这些会话和app代理方法

清单1-8,iOS后台下载的会话代理方法

#if TARGET_OS_IPHONE
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    NSLog(@"Background URL session %@ finished events.\n", session);

if (session.configuration.identifier)
    [self callCompletionHandlerForSession: session.configuration.identifier];
}

- (void) addCompletionHandler: (CompletionHandlerType) handler forSession: (NSString *)identifier
{
if ([ self.completionHandlerDictionary objectForKey: identifier]) {
    NSLog(@"Error: Got multiple handlers for a single session identifier.  This should not happen.\n");
}

[ self.completionHandlerDictionary setObject:handler forKey: identifier];
}

- (void) callCompletionHandlerForSession: (NSString *)identifier
{
CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey: identifier];

if (handler) {
    [self.completionHandlerDictionary removeObjectForKey: identifier];
    NSLog(@"Calling completion handler.\n");

    handler();
    }
}
#endif

清单1-9,iOS后台下载的app代理方法

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
    NSURLSessionConfiguration *backgroundConfigObject = [NSURLSessionConfiguration backgroundSessionConfiguration: identifier];

NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration: backgroundConfigObject delegate: self.mySessionDelegate delegateQueue: [NSOperationQueue mainQueue]];

NSLog(@"Rejoining session %@\n", identifier);

[ self.mySessionDelegate addCompletionHandler: completionHandler forSession: identifier];
}

转载请注明出处:http://blog.csdn.net/qq329735967

任何疑问欢迎Email至[email protected]

时间: 2024-11-12 19:40:29

apple官方文档翻译:使用NSURLSession(二)的相关文章

apple官方文档翻译:使用NSURLSession(一)

英文原文地址 使用NSURLSession NSURLSession和其相关的类提供了通过HTTP下载数据的API.该API提供了丰富的代理方法来支持信息身份认证,以及当app未运行时(比如,在iOS中,app挂起状态)的后台下载功能. 使用NSURLSession,客户端会创建一系列对话,每个会话都匹配一组相关的数据传输任务.例如,编码一个web浏览器,客户端可能需要为没一个标签或者窗口创建一个会话.对每个会话,客户端增加一系列任务,每个任务代表了指向一个特定URL得请求(或者HTTP重定向后

[Apple官方文档翻译]: NSURLSession Programming Guide

关于URL加载系统 这个文档描述了Foundation框架中的与URL交互的一些类和与服务器交互的标准互联网协议. 这些类统一称为URL加载系统. URL加载系统是一个一些类和协议组成的允许应用通过URL来访问内容的合集. 其中核心的类就是NSURL,它负责产生出URL和资源的位置. 为了支持这些类的运行,Foundation框架提供了很多类来使用,比如:加载内容,上传数据到服务器,管理cookie,控制返回数据缓存,处理凭证管理和认证. URL加载系统提供支持以下协议: 文件传输协议(ftp:

apple官方文档翻译:URL Loading System Programming Guide

URL Loading System Programming Guide (本文为概要描述,部分有删减) 原文链接 关于 本指南描述了使用标准Internet协议来处理Urls.与服务器通信的相关类. URL Loading System包含了一系列类和协议,来支持app访问URL上的内容.核心类是NSURL,帮助app来控制URL以及该URL指向的内容. 为了支持NSURL,apple的Foundation framework提供了丰富的类来帮助你加载URL的内容,从服务器更新数据,管理coo

Python3.2官方文档翻译-标准库概览(二)

7.5 字符串模式匹配 re模块为高级字符串成处理提供了正则表达式匹配. 对于复杂的匹配和处理,正则表达式能够提供简明优化的方法: >>> import re >>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest') ['foot', 'fell', 'fastest'] >>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat'

NServiceBus官方文档翻译(二)NServiceBus 入门

在这篇教程中我们将学习如何创建一个非常简单的由客户端向服务端发送消息的订单系统.该系统包括三个项目:Client.Server 和 Messages,我们将按照以下步骤来完成这个任务. 创建 Client 项目 创建 Messages 项目 创建 Server 项目 发送订单 运行解决方案 完整的解决方案代码可以在这里下载. 创建 Client 项目 让我们开始创建 Client 项目,它将负责发送订单请求到一个 NServiceBus 终结点(Endpoint). 以管理员权限运行 Visua

Flume官方文档翻译——Flume 1.7.0 User Guide (unreleased version)(二)

Flume官方文档翻译--Flume 1.7.0 User Guide (unreleased version)(一) Logging raw data(记录原始数据) Logging the raw stream of data flowing through the ingest pipeline is not desired behaviour in many production environments because this may result in leaking sensit

iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译

编号 iOS-Apple苹果官方文档翻译名称 博文链接地址 1 苹果API常用英语名词---iOS-Apple苹果官方文档翻译 http://www.cnblogs.com/ChenYilong/p/3495625.html 2 基本控件文档-UIWebView---iOS-Apple苹果官方文档翻译 http://www.cnblogs.com/ChenYilong/p/3556301.html 3 基本控件文档-UITextField属性---iOS-Apple苹果官方文档翻译 http:/

Oracle 12c 12.1.0.1.0管理控制文件官方文档翻译说明

Link: http://download.csdn.net/detail/rlhua/7718571 官方Link: http://docs.oracle.com/database/121/ADMIN/control.htm#ADMIN006 版本: [email protected]>select * from v$version; BANNER                                                                          

Python3.2官方文档翻译--输出格式化

第八章 标准库二 第二部分涵盖了许多更能满足专业开发人员需求的高级模块.这些模块在小脚本中很少出现. 8.1 输出格式化 Reprlib模块为大型的或深度嵌套的容器缩写显示提供了repr()函数的一个定制版本. >>> import reprlib >>> reprlib.repr(set('supercalifragilisticexpialidocious')) "set(['a', 'c', 'd', 'e', 'f', 'g', ...])"