虽然在iOS7引入NSURLSession时,就知道NSURLConnection会最终被苹果放弃,但人总是喜欢做熟悉的事情,在NSURLConnection还可以使用时,就懒得学习这新玩意了,而且本来在开发中多数时候也是使用第三方AFNetworking。最近发现iOS9中已经弃用了NSURLConnection请求网络的方法,使用NSURLConnection会警告让我这种强迫症患者抓狂,所以趁着有时间就好好学习一下NSURLSession技术吧,我喜欢学就学个明白,因此一直在深入阅读苹果官方的关于NSURLSession使用教程,因为是一边学习一边翻译一边理解,所以有些内容的翻译并不恰当,我会在这几天随着理解的深入进一步完善。今天就给大家分享一篇我翻译的苹果官方文档《URL Session Programming Guide》,官方地址:https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/UsingNSURLSession.html#//apple_ref/doc/uid/TP40013509-SW1。

  NSURLSession和它的相关类提供了我们通过HTTP协议下载数据的接口。它们提供给我们一系列代理方法来支持授权操作,并且当我们的程序退出或者在iOS中挂起时,能给我们的程序后台实现下载的能力。

  使用NSURLSession接口,我们的程序会创建一系列会话(session),每个会话负责一组相关数据的传输任务。例如,当我们在开发一个网页浏览器时,你的程序应该会为每个标签页或者窗口创建一个会话。每个会话中我们会在程序中加入多个下载任务,每个任务代表一个具体的URL请求(以及如果该原始URL有HTTP回调,也负责回调的URL请求)。

  跟大多数网络请求的接口一样,NSURLSession的接口也是支持异步的。如果你使用default(默认)工作模式,意味着使用系统提供的代理方法,你就必须要提供completion handler block 来返回请求成功或失败的结果数据。或者你也可以自定义代理对象,然后当收到服务器的数据时,任务对象就会调用这些代理方法,并将获取的数据传递过去。

  注意:完成回调block的主要设计来做为自定义代理的另一选择,如果你创建任务时使用了完成回调的方法,相应的代理方法以及传输数据可能不会被调用。

NSURLSession接口提供了状态和进度属性,并通过它的代理里传值过去。它支持取消,恢复(重启),暂停任务,并提供恢复暂停的、取消的、失败的下载的能力。

理解URL Session的概念

  会话中的任务如何执行取决于三点:会话的类型(它是由创建它的配置对象决定的)、任务的类型、当然任务创建时程序是在前台还是在后台运行。

会话类型

  NSURLSession接口提供三种类型的会话,他们是创建会话需要的configuration配置对象决定的。

1.默认会话(Default sessions):它跟Foundation框架的其他下载URLs的方法类似,他们使用一个可持续的基于磁盘的缓存并存储认证资质到用户的钥匙链中。

2.短暂会话(Ephemeral sessions):它不保存数据到磁盘中;所有的缓存,认证资质,以及其他的信息都保留在关联该会话的RAM内存中,因此,当会话作废时,这些数据都将自动丢失。

3.后台会话(Background sessions):和默认会话类似,不过它提供一个独立的进程处理所有数据的传输。后台会话也会有一些限制,可以参考下面的段落内容:是否该使用后台传输

任务类型

  NSURLSession类支持三种类型的任务:数据任务、下载任务和上传任务。

1.数据任务:使用NSData对象发送和接受数据。数据任务通常用来负责App和服务器之前交互请求。数据任务可以分片段一点一点从服务器返回数据也可以通过完成回调一次性返回全部数据。

2.下载任务:从文件中获取数据,并且支持在APP不运行时,后台下载。

3.上传任务:将文件中数据上传,并且支持Appl不运行时,后台上传。

是否该使用后台传输

  NSURLSession类支持当APP挂起时后台传输。使用后台传输需要使用后台会话配置来创建会话。

在后台会话中,实际的数据传输在独立的进程中执行,而且因为重启你的APP进程代价较大,一些功能不能实现,导致有如下限制:

1.会话必须提供为事件传递提供代理。(上传和下载的代理在进程间传输的行为是一样的)

2.仅仅支持HTTP和HTTPS协议

3.总是需要重定向

4.仅仅支持文件上传(数据(NSData)对象或者数据流上传在程序退出时会失败)。

5.如果程序在后台运行并发起后台传输,配置(configuration)对象的discretionary属性总是被设置为true。

  注意:在iOS8 和OS X10.10之前,数据任务不支持后台会话。

  在iOS和OS X 中程序再次运行时候的运行行为有些不同。

  在iOS中,当一个后台传输完成或者需要认证,如果你的APP不再运行,iOS会自动再次在后台运行你的程序,并且调用UIApplicationDelegate对象的application:handleEventsForBackgroundURLSession:completionHandler:方法,这个方法能识别导致你的APP启动的会话。你的APP应当保存completionHandler的block,使用同一标示符(identifier)创建一个后台配置对象,然后再使用这个配置对象创建会话,新的会话将自动和持续运行的后台活动关联。之后,当会话完成最后的下载任务时,它将发送给会话代理URLSessionDidFinishEventsForBackgroundURLSession:消息。你的会话代理这时候应该使用的你刚刚保存的completionHandler的block对象。

  在iOS和OS X中,APP最后一次前台运行时每个会话会有一个标示符,当用户再次运行APP,你的APP应当立即使用相同的标示符(identifier)创建后台配置对象,然后为每个配置对象创建会话。新的会话将自动和持续运行的后台活动关联。

注意:你必须为每个标示符(当你在创建配置对象时配置的)创建会话,多会话使用同一标示符的实现还没有定义。

当你的APP挂起时,每完成一个任务,代理的 URLSession:downloadTask:didFinishDownloadingToURL: 方法将会被调用,并将最新下载的文件的任务以及URL传递过来。

  当任务需要认证的情况相似,NSURLSession对象调用代理的URLSession:task:didReceiveChallenge:completionHandler: 方法或者   URLSession:didReceiveChallenge:completionHandler:方法。

  当网络出错URL加载系统将自动重新尝试在后台会话中下载和上传任务。因此不需要使用reachability接口来判断何时重新尝试失败的任务。

可以查看使用NSURLSession实现后台传输的例子:Simple Background Transfer

生命周期和代理交互

  完全理解会话的生命周期对使用NSURLSession类是有帮助的,包括会话和代理是如何相互作用的,代理调用的顺序,服务器返回重定向会做些生什么,当你的APP恢复失败的下载会做些什么等等。

为了更好的理解URL会话的声明周期可以阅读Life Cycle of a URL Session.

NSCopying行为

会话和任务遵守NSCopying协议:

1.当你的APP复制一个会话或者任务对象时,你获得的是同一个对象。

2.当你的APP复制配置对象时,你获得的是一个新的副本,因此你可以完全修改。

代理类的声明样例

Listing 1-1 中显示代理声明的代码片段

Listing 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接口提供了非常多的配置选项:

1.可以为每个会话设置缓存、cookies、认证、协议的私有存储支持。

2.可以为一个请求(任务)或者一组请求(会话)授权。

3.通过URL上传和下载的文件,可以将数据内容(文件内容)和元数据(文件的URL和设置)分开。

4.为每个主机配置最大的连接数量。

5.设置每个下载资源的下载超时时间

6.最低和最高TLS版本支持

7.自定义代理(proxy)字典

8.控制cookie的规则

9.控制HTTP通道行为。

  因为多数配置已经包含在每个配置对象中,我们可以使用这些通用的配置。当你实例化一个会话对象时候,你可以设置如下内容:

1.管理会话和该会话的任务的配置对象。

2.可选地,当接收到数据时候用来处理数据的代理对象以及其他跟会话和任务相关的代理,比如服务器认证代理,判断是否将一个资源加载请求转移到下载的代理等的。

如果你不提供代理,

  如果你不提供代理,NSURLSession使用系统提供的代理方法。在这种情况下,你可以方便地使用NSURLSession以及使用sendAsynchronousRequest:queue:completionHandler:方法。

  注意:如果你的程序需要执行后台传输,你必须自定义代理。在你初始化会话对象后,如果不创建新的会话,你不能修改配置和代理。

Listing 1-2 子展示如何创建普通、短暂、后台会话。

Listing 1-2 创建和配置会话

#if TARGET_OS_IPHONE

    self.completionHandlerDictionary = [NSMutableDictionary dictionaryWithCapacity:];

#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:];

    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:  diskCapacity:  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]];

  除了后台配置外,你可以重复利用会话配置创建其他会话。(因为不同的后台会话不能使用同一标示符,所以不能重复利用后台配置)。

  你可以随时安全地修改配置对象,当你创建一个对象时,对象会对配置对象执行一次深复制,因此对配置的修改只会影响新的会话,而不会影响已经存在的会话。例如,你可以创建一个新的会话,当APP是WiFi连接时才去检索数据内容,如 Listing 1-3所示:

Listing 1-3:使用同一配置对象创建新的会话

    ephemeralConfigObject.allowsCellularAccess = NO;

    // ...

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

使用系统自带的代理类获取资源

  使用NSURLSession最直接方式就是使用NSURLSession的sendAsynchronousRequest:queue:completionHandler: 方法。使用这个方法,你仅仅需要段代码:

1.创建配置对象和创建会话对象的代码

2.从completion handler Block获取到数据后需要执行的操作。

  使用系统提供的代理,我们只需要为每个请求添加一行代码就可以获取一个URL的数据。List1-4展示这个方式的简单用例。

  注意:系统提供代理类仅仅提供有限的自定义的网络操作行为。如果你的APP需要更复杂的URL请求,比如自定义授权或者后台下载,这个技术就不太适用了。要了解哪些情况需要你完全自定义代理,可以查看URL 会话的生命周期段落

Listing 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];

使用自定义代理获取数据

  如果你使用自定义的代理获取数据,代理至少必须实现以下方法:

1.URLSession:dataTask:didReceiveData:将一个请求转成data数据,一次一个片段。

2.URLSession:task:didCompleteWithError:数据完全下载时调用。

  如果你的APP需要在 URLSession:dataTask:didReceiveData: 返回前使用获取的数据data,那么你需要编写代码保存获取到的数据。

例如,网页浏览器可能需要将最新获取的数据和之前的数据进行拼接后再处理。为了实现这个功能,可以使用一个字典存储NSMutableData对象,然后将每次获取到的数据保存在这个对象里,然后我们可以使用 appendData:方法追加最新获取的数据。

Listing 1-5 显示如何创建和启动一个数据任务

Listing 1-5  数据任务样例

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

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

    [dataTask resume];

下载文件

  下载一个文件跟获取数据相似,你的程序需要实现下面这个代理方法:

URLSession:downloadTask:didFinishDownloadingToURL: 它会把一个临时文件内的URL提供给你的程序,这个临时文件存储着你要下载的内容。

  重要提示:在这个方法返回前,文件要们被打开使用,要么把它移动到其他永久目录中,因为当方法返回时,临时文件如果还存在它将会被删除。

1.URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: 提供给你的APP关于下载进度的状态信息。

2.URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes: 通知你的APP恢复一个先前失败的下载成功

3.URLSession:task:didCompleteWithError: 通知你的APP下载失败

  如果你计划后台会话下载,当你的程序退出时下载仍然会继续。如果你计划标准或者短暂下载,你的程序需要重启后下载必须重新开始。

  如果传输失败,你的代理方法 URLSession:task:didCompleteWithError:将会调用,并传递NSError对象。如果任务可以恢复,那userInfo对象包含NSURLSessionDownloadTaskResumeData键的值,你的APP可以将返回的恢复数据要么给 downloadTaskWithResumeData: 或者downloadTaskWithResumeData:completionHandler:方法来创建一个新的下载任务来重新下载。

Listing 1-6展示一个下载较大文件的例子。Listing 1-7展示一个下载任务的代理方法的例子。

Listing 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];

Listing 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);

}

上传POST请求体数据内容

  你的APP可以使用三种方式提供给HTTP作为POST请求的请求体:NSData对象,文件,数据流。总的来说,你的APP应当:

1.NSData.如果内存中已经有上传数据的NSData对象那就使用它,没必要丢弃不用。

2.文件。如果你要上传的文件在磁盘上那就直接使用文件。如果你要使用后台传输,或者因为程序需要,你将数据写到磁盘以便释放内存。

3.数据流。如果你从网络获取数据或者将NSURLConnection请求的数据(使用数据流)转移过来,那你可能需要使用数据流。

  不管你使用哪个方式,如果你的APP使用自定义的会话代理,代理需要实现方法URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend: 来获取上传进度信息。

如果你的APP使用数据流作为请求体,那如果自定义代理,APP必须实现URLSession:task:needNewBodyStream: 方法,你可以在这里找到更多信息:Uploading Body Content Using a Stream.

使用NSData对象上传请求体内容

  通过NSData对象上传请求体内容,你的APP可以调用uploadTaskWithRequest:fromData:或者uploadTaskWithRequest:fromData:completionHandler: 方法来创建上传任务,并且将NSData对象作为通过fromData参数传递。会话对象根据NSData对象计算 请求头里Content-Length的值的大小。

  你的APP必须提供提供服务器必须要的请求头信息—-比如,content type,作为URL请求对象的部分。

通过文件上传请求体内容

  通过文件上传请求体内容,你的APP可以调用uploadTaskWithRequest:fromFile: 或者uploadTaskWithRequest:fromFile:completionHandler: 方法来创建上传任务,并将文件的URL传递过去,任务需要读取文件的内容。

  会话根据数据对象的大小计算Content-Length的值的大小。如果你的APP没有设置Content-Type的值,会话会自动提供值。你的APP必须提供提供服务器必须要的请求头信息作为URL请求对象的部分。

通过数据流上传请求体数据内容

  通过数据流上传请求体数据内容,你的APP需要调用uploadTaskWithStreamedRequest:方法来创建上传任务。你的APP需要提供数据流给请求对象,任务需要读取它的内容。

  你的APP必须提供提供服务器必须要的请求头信息—-比如,content type,作为URL请求对象的部分。

  因为会话不能倒带来重新读取数据,当会话需要重新请求时(比如,如果授权失败),那你的APP必须负责提供一个新的数据流。为了达到目的,你的程序需要实现URLSession:task:needNewBodyStream: 方法。当方法被调用时,你的APP必须通过各种方式获取或创建新的数据流,并将它传递到完成处理block里。

  注意:因为当通过数据流上传时,你的APP必须实现 URLSession:task:needNewBodyStream:代理方法,这个技术和系统提供的代理方法不兼容

使用下载任务上传文件

  通过下载任务上传请求提内容,当你的APP创建下载请求时,APP必须传递NSData对象或者数据流作为NSURLRequest对象的部分内容。

如果使用数据流,如果授权失败了,你的APP必须实现URLSession:task:needNewBodyStream:方法,将新的数据内容传递过去。这个方法的更多信息可以在这里找到: Uploading Body Content Using a Stream

处理授权和自定义TLS链的验证

  如果远程服务器返回状态信息,提示你需要提供授权信息并且当然授权需要一定连接级别(比如:SSL客户端认证),NSURLSession调用授权代理方法。

1.会话级别的连接验证————NSURLAuthenticationMethodNTLM, NSURLAuthenticationMethodNegotiate, NSURLAuthenticationMethodClientCertificate, 或者 NSURLAuthenticationMethodServerTrust——-NSURLSession对象调用会话代理方法 URLSession:didReceiveChallenge:completionHandler:。如果你的APP没有提供会话代理方法,那NSURLSession对象调用任务代理方法URLSession:task:didReceiveChallenge:completionHandler: 来进行验证。

2.非会话级别的连接验证。NSURLSession提供会话代理的方法URLSession:task:didReceiveChallenge:completionHandler:来处理验证请求。如果你的APP提供了会话代理,你需要处理授权,然后必须在任务级别处理授权或者明确地为一个任务级别的提供处理handler,并调用每个会话的handler。会话代理方法URLSession:didReceiveChallenge:completionHandler: 不会在非会话级别的验证中调用。

  注意:明确透明底处理Kerberos授权

  当在数据流上传中任务授权失败,任务不会倒带并安全地重复利用之前的数据流。NSURLSession对象会调用URLSession:task:needNewBodyStream: 代理方法来获取一个新的 NSInputStream对象的数据流,并作为请求体数据发送新的上传请求。(如果任务上传是通过文件或者NSData对象上传数据,这个代理方法不会被调用)。

  获取NSURLSession授权的更多信息,可以阅读 Authentication Challenges and TLS Chain Validation

处理iOS后台活动

  如果你在iOS中使用NSURLSession对象,当下载完成你的APP会自动重启。你的APP的application:handleEventsForBackgroundURLSession:completionHandler: 对象负责再次创建相应的会话,保存完成处理block,并可以在会话代理方法URLSessionDidFinishEventsForBackgroundURLSession: 调用这个block。

Listing 1-8 and Listing 1-9分别展示了这些会话和APP代理的方法。

Listing 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

Listing 1-9  后台下载有关的App delegate方法

- (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];

}

NSURLSession的使用的更多相关文章

  1. [OC] NSURLSession

    有的程序员老了,还没听过NSURLSession 有的程序员还嫩,没用过NSURLConnection 有的程序员很单纯,他只知道AFN. NSURLConnection在iOS9被宣布弃用,NSUR ...

  2. NSURLSession详解

    导语 现在NSURLConnection在开发中会使用的越来越少,iOS9已经将NSURLConnection废弃,现在最低版本一般适配iOS7,所以也可以使用. NSURLConnection相对于 ...

  3. iOS开发之Alamofire源码解析前奏--NSURLSession全家桶

    今天博客的主题不是Alamofire, 而是iOS网络编程中经常使用的NSURLSession.如果你想看权威的NSURLSession的东西,那么就得去苹果官方的开发中心去看了,虽然是英文的,但是结 ...

  4. [swift]NSURLSession

    一.简单说明 在iOS9.0之后,以前使用的NSURLConnection过期,苹果推荐使用NSURLSession来替换NSURLConnection完成网路请求相关操作. NSURLSession ...

  5. iOS网络2——NSURLSession使用详解

    原文在此 一.整体介绍 NSURLSession在2013年随着iOS7的发布一起面世,苹果对它的定位是作为NSURLConnection的替代者,然后逐步将NSURLConnection退出历史舞台 ...

  6. NSURLSession网络请求

    个人感觉在网上很难找到很简单的网络请求.或许是我才疏学浅 ,  所有就有了下面这一段 , 虽然都是代码 , 但是全有注释 . //1/获取文件访问路径 NSString *path=@"ht ...

  7. 网络第三节——NSURLSession

    有的程序员老了,还没听过NSURLSession有的程序员还嫩,没用过NSURLConnection有的程序员很单纯,他只知道AFN. NSURLConnection在iOS9被宣布弃用,NSURLS ...

  8. 网络请求 __ NSURLSession

    首先配置into.plist文件 1. 添加 App Transport Security Settings , Type栏自动变为Dictionary 2. 点击左边箭头,使之向下,点击右边加号,添 ...

  9. NSURLSession总结

    NSURLSession(会话)(ios7新增加) //英译  Session:会议,讲话 configuration:结构,配置 expect:预期 resume:取得 suspend:推迟 pro ...

  10. NSURLSession访问网络数据

    1.NSMutableURLRequest的设置 //创建NSMutableURLRequest对象 NSMutableURLRequest *request = [NSMutableURLReque ...

随机推荐

  1. Gevent的长轮询实现方法详解

    长轮询 1.浏览网页时,浏览器会传HTTP 请求到服务器,服务器会根据请求将网页的内容传给浏览器,但是在很多的情况下,使用者会需要看到最新的即时性资讯,例如观看股票市场行情,而在以前只能靠着重新载入网 ...

  2. JavaScript设计模式与开发实践 - 观察者模式

    概述 观察者模式又叫发布 - 订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个目标对象(为了方便理解,以下将观察者对象叫做订阅者,将目标对象叫做 ...

  3. android用shape给linearLayout设置边框,怎样只保留底部或顶部的边框,把其它三个方向的边框去掉呢?

    http://bbs.csdn.net/topics/390485215 这种方法只是两个颜色块相减而已 <?xml version="1.0" encoding=" ...

  4. China Brain Project: Basic Neuroscience, Brain Diseases, and Brain-Inspired Computing

    日前,中国科学院神经科学研究所.中国科学院脑科学与智能技术卓越创新中心.香港科技大学生命科学部和分子神经科学国家重点实验室.中国科技大学自动化研究所在 Cell 上联合发表了一篇概述论文<Chi ...

  5. 关于ipxe启动的几个疑问

    关于ipxe启动的几个疑问 http://bbs.wuyou.net/forum.php?mod=viewthread&tid=373026&page=1&extra=#pid ...

  6. 通用 PE 工具箱1.9.6(XP内核)by Uepon(李培聪)

    通用 PE 工具箱1.9.6(XP内核)by Uepon(李培聪)官网:http://hi.baidu.com/uepon?page=21.8版论坛帖子:http://bbs.wuyou.net/fo ...

  7. 【bzoj1043】下落的圆盘

    [bzoj1043]下落的圆盘 题意 有n个圆盘从天而降,后面落下的可以盖住前面的.求最后形成的封闭区域的周长.看下面这副图, 所有的红色线条的总长度即为所求. \(1\leq n\leq 1000\ ...

  8. python 练习 15

    #!/usr/bin/python # -*- coding: UTF-8 -*- for i in range(1,10): for j in range(1,10): result = i * j ...

  9. robot API笔记4

    robot.htmldata包 包编写HTML格式的输出文件. 这个包被认为是稳定的但不是公共API的一部分. robot.htmldata.htmlfilewriter module classro ...

  10. 《javascript高级程序设计》第八章 The Browser Object Model

    8.1 window 对象 8.1.1 全局作用域 8.1.2 窗口关系及框架 8.1.3 窗口位置 8.1.4 窗口大小 8.1.5 导航和打开窗口 8.1.6 间歇调用和超时调用 8.1.7 系统 ...