标签:
下面的代码片段基于清单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提供了大量的配置选项:
大部分的配置都在一个configuration对象中设置,可以通用一些基本设置.初始化一个会话对象(session object)需要指定如下信息:
如果没有指定一个代理,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中实现两处代码:
使用系统提供的代理,你可以每个请求只用一行代码来抓取特定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];
使用自定义代理检索数据必须实现下列方法:
如果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应当实现以下的代理方法:
如果将下载任务安排在后台会话中,在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应当:
无论你选择了哪种方法,如果app提供了自定义代理,都应该实现URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: 方法来获取上传进度.
此外,如果app使用流作为请求体,还必须提供一个自定义会话代理实现URLSession:task:needNewBodyStream:方法,详细描述在通过流上传数据
使用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调用会调用一个认证相关的代理方法.
更多信息参见 Authentication Challenges and TLS Chain Validation.
在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至329735967@qq.com
标签:
原文地址:http://blog.csdn.net/qq329735967/article/details/45151387