您的位置:首页 > 其它

NSNSURLSession详解

2016-01-15 19:50 127 查看
作为Core Foundation / CFNetwork 框架的APIs之上的一个抽象,NSURLConnection这个名字,实际上指的是一组构成Foundation框架中URL加载系统的相互关联的组件:NSURLRequest,NSURLResponse,NSURLProtocol,NSURLCache,NSHTTPCookieStorage,NSURLCredentialStorage,以及和它同名的NSURLConnection。

NSURLRequest对象被传递给一个NSURLConnection对象。委托(遵守从前的非正式<NSURLConnectionDelegate> 和 <NSURLConnectionDataDelegate>协议)作为一个NSURLResponse异步响应,任何相关的NSData从服务器发送。

一个请求发送到服务器前,共享的高速缓存先被访问,然后根据策略(policy)和可用性(availability),一个缓存的响应可能立即透明地返回,如果所有缓存的响应都不可用,则该请求根据选项,被用于为任何后续请求缓存它的响应。

在协商发送一个请求到服务器的过程中,该服务器可发出验证质询,这可以由共享的cookie,证书存储(credential storage)或通过连接委托自动处理。必要的时候,为了无缝地改变装载行为,传出请求也可以被注册的NSURLProtocol对象截获。

与NSURLConnection类似,除了同名类NSURLSession,NSURLSession也是指一组相互依赖的类。NSURLSession包括与之前相同的组件,例如NSURLRequest, NSURLCache等。NSURLSession的不同之处在于,它把 NSURLConnection替换为NSURLSession, NSURLSessionConfiguration,以及3个NSURLSessionTask的子类:NSURLSessionDataTask, NSURLSessionUploadTask,
和NSURLSessionDownloadTask.

与NSURLConnection相比,NSURLSession最直接的改善就是提供了配置每个会话的缓存,协议,cookie和证书政策(credential policies),甚至跨应用程序共享它们的能力。这使得框架的网络基础架构和部分应用程序独立工作,而不会互相干扰。每一个NSURLSession对象都是根据一个NSURLSessionConfiguration初始化的,该NSURLSessionConfiguration指定了上面提到的政策,以及一系列为了提高移动设备性能而专门添加的新选项。

NSURLSession的另一重要组成部分是会话任务,它负责处理数据的加载,以及客户端与服务器之间的文件和数据的上传下载服务。NSURLSessionTask与NSURLConnection是及其相似的,因为它负责加载数据,而主要的区别在于,任务共享它们父类NSURLSession的共同委托(common delegate)。

NSURLSessionTask是一个抽象子类,它有三个具体的子类是可以直接使用的:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。这三个类封装了现代应用程序的三个基本网络任务:获取数据,比如JSON或XML,以及上传下载文件。

当一个NSURLSessionDataTask完成时,它具有关联的数据,而一个NSURLSessionDownloadTask完成时,它具有一个已下载文件的临时文件路径。 NSURLSessionUploadTask 继承了 NSURLSessionDataTask,因为服务器响应一个上传请求时,往往伴随着相关联的数据。 所有任务均可撤销,也可以暂停和恢复。当一个下载任务被取消时,它可以选择创建恢复数据,然后可以传递给下一次新创建的下载任务,以便继续之前的下载。

不同于直接使用alloc-init‘d初始化方法,任务是由一个NSURLSession创建的。每个任务的构造方法都对应一个版本,有或者没有completionHandler属性,例如:–dataTaskWithRequest: 和 –dataTaskWithRequest:completionHandler:。这与NSURLConnection的 -sendAsynchronousRequest:queue:completionHandler:类似,通过指定completionHandler属性创建并使用一个隐含的委托,而不是使用任务的会话。在任何一种任务会话委托的默认行为需要被重写的情况下,这种不太方便的非completionHandler的变体将需要被使用。

iOS5中,NSURLConnection添加了sendAsynchronousRequest:queue:completionHandler:方法,这大大简化了一次性请求的使用,同时可以作为sendSynchronousRequest:returningResponse:error::的异步替代品。

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

NSURLRequest *request = [NSURLRequest requestWithURL:URL];

[NSURLConnection sendAsynchronousRequest:request

queue:[NSOperationQueue mainQueue]

completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

// ...

}];

NSURLSession与它的任务构造方法在此模式上迭代。在执行resume方法前,该任务对象为了进行进一步的配置而返回,而不是立即执行resume方法。

数据任务可以通过NSURL或NSURLRequest创建(前者是一个标准GET请求URL的快捷方式)。

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

NSURLRequest *request = [NSURLRequest requestWithURL:URL];

NSURLSession *session = [NSURLSession sharedSession];

NSURLSessionDataTask *task = [session dataTaskWithRequest:request

completionHandler:

^(NSData *data, NSURLResponse *response, NSError *error) {

// ...

}];

[task resume];

上传任务也可以通过一个请求以及一个需要上传的本地文件的URL对应的NSData对象创建。

NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];

NSURLRequest *request = [NSURLRequest requestWithURL:URL];

NSData *data = ...;

NSURLSession *session = [NSURLSession sharedSession];

NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request

fromData:data

completionHandler:

^(NSData *data, NSURLResponse *response, NSError *error) {

// ...

}];

[uploadTask resume];

下载任务也需要一个请求,但不同之处在于它们的completionHandler。数据和上传任务在完成时立即返回,但下载任务将数据写入本地的临时文件。completionHandler有责任将文件从它的临时位置移动到一个永久位置,这个永久位置就是块的返回值。

NSURL *URL = [NSURL URLWithString:@"http://example.com/file.zip"];

NSURLRequest *request = [NSURLRequest requestWithURL:URL];

NSURLSession *session = [NSURLSession sharedSession];

NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request

completionHandler:

^(NSURL *location, NSURLResponse *response, NSError *error) {

NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];

NSURL *documentsDirectoryURL = [NSURL fileURLWithPath:documentsPath];

return [documentsDirectoryURL URLByAppendingPathComponent:[[response URL] lastPathComponent]];

}];

[downloadTask resume];

NSURLSession & NSURLConnection Delegate Methods

以下是一些具体的观察:

NSURLSession同时具有用来处理身份验证挑战会话和任务委托方法。这个会话的委托方法处理连接级别的问题,如服务器信任和客户端证书的评估,

NSURLConnection由两个方法可以表明一个请求已经完成(NSURLConnectionDataDelegate -connectionDidFinishLoading: 和 NSURLConnectionDelegate -connection:didFailWithError:),而NSURLSession只有一个委托方法(NSURLSessionTaskDelegate -URLSession:task:didCompleteWithError:)。

与NSURLConnection使用的 long long类型相比,委托方法指定在NSURLSession中一定数量的字节传输使用int64_t类型的参数。

NSURLSession在Foundation框架对于委托方法的completionHandler:参数使用上 ,引入了一种新的模式。这允许委托方法可以安全地在主线程以非阻塞方式运行;委托可以简单地在后台运行dispatch_async ,然后在完成时调用completionHandler。同时,它可以有效地拥有多个返回值,不需要使用笨拙的参数指针。就NSURLSessionTaskDelegate的URLSession:task:didReceiveChallenge:completionHandler:方法而言,completionHandler接受两个参数:身份验证质询的处理( the
authentication challenge disposition)以及需用使用的证书(如果适用)。

想要查看更多关于会话任务的信息,可以查看 WWDC Session 705: “What’s New in Foundation Networking”

NSURLSessionConfiguration对象用于初始化NSURLSession对象。展开请求级别中与NSMutableURLRequest相关的可供选择的方案,我们可以看到NSURLSessionConfiguration对于会话如何产生请求,提供了相当多的控制和灵活性。从网络访问性能,到cookie,安全性,缓存策略,自定义协议,启动事件设置,以及用于移动设备优化的几个新属性,你会发现你一直在寻找的,正是NSURLSessionConfiguration。

会话在初始化时复制它们的配置,NSURLSession有一个只读的配置属性,使得该配置对象上的变化对这个会话的政策无效。配置在初始化时被读取一次,之后都是不会变化的。

NSURLSessionConfiguration有三个类构造函数,这很好地说明了NSURLSession是为不同的用例而设计的。

+ defaultSessionConfiguration返回标准配置,这实际上与NSURLConnection的网络协议栈是一样的,具有相同的共享NSHTTPCookieStorage,共享NSURLCache和共享NSURLCredentialStorage。

+ ephemeralSessionConfiguration返回一个预设配置,没有持久性存储的缓存,Cookie或证书。这对于实现像秘密浏览功能的功能来说,是很理想的。

+ backgroundSessionConfiguration:独特之处在于,它会创建一个后台会话。后台会话不同于常规的,普通的会话,它甚至可以在应用程序挂起,退出,崩溃的情况下运行上传和下载任务。初始化时指定的标识符,被用于向任何可能在进程外恢复后台传输的守护进程提供上下文。

想要查看更多关于后台会话的信息,可以查看WWDC Session 204: “What’s New with Multitasking”

NSString *userPasswordString = [NSString stringWithFormat:@"%@:%@", user, password];

NSData * userPasswordData = [userPasswordString dataUsingEncoding:NSUTF8StringEncoding];

NSString *base64EncodedCredential = [userPasswordData base64EncodedStringWithOptions:0];

NSString *authString = [NSString stringWithFormat:@"Basic: %@", base64EncodedCredential];

NSString *userAgentString = @"AppName/com.example.app (iPhone 5s; iOS 7.0.2; Scale/2.0)";

configuration.HTTPAdditionalHeaders = @{@"Accept": @"application/json",

@"Accept-Language": @"en",

@"Authorization": authString,

@"User-Agent": userAgentString};

networkServiceType对标准的网络流量,网络电话,语音,视频,以及由一个后台进程使用的流量进行了区分。大多数应用程序都不需要设置这个。

allowsCellularAccess 和 discretionary 被用于节省通过蜂窝连接的带宽。建议在使用后台传输的时候,使用discretionary属性,而不是allowsCellularAccess属性,因为它会把WiFi和电源可用性考虑在内。

timeoutIntervalForRequest 和 timeoutIntervalForResource指定了请求以及该资源的超时时间间隔。许多开发人员试图使用timeoutInterval去限制发送请求的总时间,但这误会了timeoutInterval的意思:报文之间的时间。timeoutIntervalForResource实际上提供了整体超时的特性,这应该只用于后台传输,而不是用户实际上可能想要等待的任何东西。

HTTPMaximumConnectionsPerHost 是 Foundation 框架中URL加载系统的一个新的配置选项。它曾经被用于NSURLConnection管理私人连接池。现在有了NSURLSession,开发者可以在需要时限制连接到特定主机的数量。

HTTPShouldUsePipelining 也出现在NSMutableURLRequest,它可以被用于开启HTTP管道,这可以显着降低请求的加载时间,但是由于没有被服务器广泛支持,默认是禁用的。

sessionSendsLaunchEvents 是另一个新的属性,该属性指定该会话是否应该从后台启动。

connectionProxyDictionary指定了会话连接中的代理服务器。同样地,大多数面向消费者的应用程序都不需要代理,所以基本上不需要配置这个属性。

关于连接代理的更多信息可以在 CFProxySupport Reference 找到。

应用实例代码(需要已经配置服务器)

#import "ViewController.h"

#define kBoundary @"kboundary"

@interface
ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {

[super
viewDidLoad];

// [self getHttpBody];

[self
getFileTypeWithFilePath:@"/Users/teacher/Desktop/01-作业.mp4"];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

{

//
文件上传 POST 请求

// 1.创建请求

NSURL *url = [NSURL
URLWithString:@"http://127.0.0.1/upload/upload.php"];

NSMutableURLRequest *request = [NSMutableURLRequest
requestWithURL:url];

// 1.设置请求方法

request.HTTPMethod =
@"POST";

NSString *type = [NSString
stringWithFormat:@"multipart/form-data; boundary=%@",kBoundary];

// 2.告诉服务器,有文件参数.边界格式:kBoundary.

[request setValue:type
forHTTPHeaderField:@"Content-Type"];

// 3.设置请求体

request.HTTPBody = [self
getHttpBodyWithFilePath:@"/Users/teacher/Desktop/123.jpg"
FileKey:@"userfile"
FileName:@"123"];

// 2.
发送请求

[[[NSURLSession
sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *
_Nullable data, NSURLResponse *
_Nullable response,
NSError * _Nullable error) {

//

NSLog(@"%@",[[NSString
alloc] initWithData:data
encoding:NSUTF8StringEncoding]);

}] resume];

}

// 获取文件类型 :
给文件发送一个本地请求,获得文件类型

// filePath: 文件路径

-(NSURLResponse *)getFileTypeWithFilePath:(NSString *)filePath

{

//
本地请求路径

NSString *urlString = [NSString
stringWithFormat:@"file://%@",filePath];

//
百分号转译

urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet
URLQueryAllowedCharacterSet]];

NSURLRequest *request = [NSURLRequest
requestWithURL:[NSURL
URLWithString:urlString]];

//
本地请求,同步请求.

NSURLResponse *response =
nil;

// 同步请求方法.
返回值,就是需要的内容(文件内容)

[NSURLConnection
sendSynchronousRequest:request returningResponse:&response
error:NULL];

// response.MIMEType :文件类型

// response.suggestedFilename :建议的文件名称

NSLog(@"%@ %@",response.MIMEType,response.suggestedFilename);

return response;

}

// 拼接上传文件的请求体格式.

// 参数:

// 1. filePath : 文件路径

// 2. fileKey : 服务器接收文件参数的 key
值.

// 3. fileName : 用户指定文件在服务器中保存的名称.

-(NSData *)getHttpBodyWithFilePath:(NSString *)filePath FileKey:(NSString *)fileKey FileName:(NSString *)fileName

{

NSMutableData *data = [NSMutableData
data];

//
上边界 \r\n 安全的换行.

NSMutableString *headerStrM = [NSMutableString
stringWithFormat:@"--%@\r\n",kBoundary];

// 访问本地文件,获取本地文件信息

NSURLResponse *response = [self
getFileTypeWithFilePath:filePath];

if (!fileName) {

fileName = response.suggestedFilename;
// 默认的文件名称.

}

//
如果 fileName == nil ,直接使用建议的文件名称.

[headerStrM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",fileKey,fileName];

// 告诉服务器上传文件的文件类型.一般给一个正确地类型.

[headerStrM appendFormat:@"Content-Type: %@\r\n\r\n",response.MIMEType];

[data appendData:[headerStrM
dataUsingEncoding:NSUTF8StringEncoding]];

//
文件内容

NSData *fileData = [NSData
dataWithContentsOfFile:filePath];

[data appendData:fileData];

//
下边界

NSMutableString *footerStrM = [NSMutableString
stringWithFormat:@"\r\n--%@--",kBoundary];

[data appendData:[footerStrM
dataUsingEncoding:NSUTF8StringEncoding]];

return data;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: