IOS 开发 网络详解(二)

 

 

NSURLSesstion

 

NSURLConnection在IOS2出现,在iOS9被宣布弃用,NSURLSession从13年发展到现在,终于迎来了它独步江湖的时代.NSURLSession是苹果在iOS7后为HTTP数据传输提供的一系列接口,比NSURLConnection强大,坑少,好用.今天从使用的角度介绍下.

 

使用NSURLSession,拢共分两步:

第一步 通过NSURLSession的实例创建task
第二部 执行task
既然两步里面都出现了task,就先说说它吧.

NSURLSessionTask可以简单理解为任务:如数据请求任务,下载任务,上传任务and so on.我们使用的是他的子类们:

NSURLSessionTask(抽象类)
NSURLSessionDataTask
NSURLSessionUploadTask
NSURLSessionDownloadTask
从这几个子类的名字就可以大概猜出他们的作用了.接下来我们就从不同类型的任务出发,来使用session.
NSURLSessionDataTask

字面上看是和数据相关的任务,但其实dataTask完全可以胜任downloadTask和uploadTask的工作.这可能也是我们使用最多的task种类.

简单GET请求

如果请求的数据比较简单,也不需要对返回的数据做一些复杂的操作.那么我们可以使用带block

// 快捷方式获得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];
Tips:

所有类型的task都要调用resume方法才会开始进行请求.
简单POST请求 POST和GET的区别就在于request,所以使用session的POST请求和GET过程是一样的,区别就在于对request的处理. 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代理方法
NSURLSessionDataDelegate代理方法

NSURLSession提供了block方式处理返回数据的简便方式,但如果想要在接收数据过程中做进一步的处理,仍然可以调用相关的协议方法.NSURLSession的代理方法和NSURLConnection有些类似,都是分为接收响应、接收数据、请求完成几个阶段.

// 使用代理方法需要设置代理,但是session的delegate属性是只读的,要想设置代理只能通过这种方式创建session
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同样提供了通过NSURL和NSURLRequest两种方式来初始化并通过block进行回调的方法.下面以NSURL初始化为例:

SURLSession *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];

简单的上传

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

 [self.session uploadTaskWithRequest:request
                            fromData:body
                   completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
 NSLog(@"-------%@", [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]);
 }];

断点续传

 

 

其他

此外,task们自身有都拥有下面几个方法

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

resume方法不仅可以启动任务,还可以唤醒suspend状态的任务

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

 

NSURLSessionConfiguration

简单地说,就是session的配置信息.如:

NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
// 超时时间
config.timeoutIntervalForRequest = 10;
// 是否允许使用蜂窝网络(后台传输不适用)
config.allowsCellularAccess = YES;
// 还有很多可以设置的属性
有没有发现我们使用的Configuration都是默认配置:[NSURLSessionConfiguration defaultSessionConfiguration],其实它的配置有三种类型:

+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier
表示了NSURLSession几种不同的工作模式.
默认的配置会将缓存存储在磁盘上,第二种瞬时会话模式不会创建持久性存储的缓存,第三种后台会话模式允许程序在后台进行上传下载工作.

除了支持任务的暂停和断点续传,我觉得NSURLSession之于NSURLConnection的最伟大的进步就是支持后台上传下载任务,这又是一个可以深入讨论的话题.但在这方面我还没有进行深入的研究,待后续了解之后另行开贴.

 

ASI

 同步请求:

 

发起一个同步请求

同步意为着线程阻塞,在主线程中使用此方法会使应用Hang住而不响应任何用户事件。所以,在应用程序设计时,大多被用在专门的子线程增加用户体验,或用异步请求代替(下面会讲到)。
- (IBAction)grabURL:(id)sender
{
  NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
  ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
  [request startSynchronous];
  NSError *error = [request error];
  if (!error) {
    NSString *response = [request responseString];
  }
}

 

 

异步请求:

 

创建一个异步请求

异步请求的好处是不阻塞当前线程,但相对于同步请求略为复杂,至少要添加两个回调方法来获取异步事件。
下面异步请求代码完成上面同样的一件事情:
- (IBAction)grabURLInBackground:(id)sender
{
   NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
   ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
   [request setDelegate:self];
   [request startAsynchronous];
}
 
- (void)requestFinished:(ASIHTTPRequest *)request
{
   // Use when fetching text data
   NSString *responseString = [request responseString];
 
   // Use when fetching binary data
   NSData *responseData = [request responseData];
}
 
- (void)requestFailed:(ASIHTTPRequest *)request
{
   NSError *error = [request error];
}

 

队列请求:

队列请求

提供了一个对异步请求更加精准丰富的控制。
如,可以设置在队列中,同步请求的连接数。往队列里添加的请求实例数大于maxConcurrentOperationCount时,请求实例将被置为等待,直到前面至少有一个请求完成并出列才被放到队列里执行。
也适用于当我们有多个请求需求按顺序执行的时候(可能是业务上的需要,也可能是软件上的调优),仅仅需要把maxConcurrentOperationCount设为“1”。
- (IBAction)grabURLInTheBackground:(id)sender
{
   if (![self queue]) {
      [self setQueue:[[[NSOperationQueue alloc] init] autorelease]];
   }
 
   NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
   ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
   [request setDelegate:self];
   [request setDidFinishSelector:@selector(requestDone:)];
   [request setDidFailSelector:@selector(requestWentWrong:)];
   [[self queue] addOperation:request]; //queue is an NSOperationQueue
}
 
- (void)requestDone:(ASIHTTPRequest *)request
{
   NSString *response = [request responseString];
}
 
- (void)requestWentWrong:(ASIHTTPRequest *)request
{
   NSError *error = [request error];
}
创建NSOperationQueue,这个Cocoa架构的执行任务(NSOperation)的任务队列。我们通过ASIHTTPRequest.h的源码可以看到,
此类本身就是一个NSOperation的子类。也就是说它可以直接被放到"任务队列"中,并被执行。上面的代码队了队列的创建与添加操作外,其它代码与上一例一样。

 

上传:

向服务器端上传数据

ASIFormDataRequest ,模拟 Form表单提交,其提交格式与 Header会自动识别。
没有文件:application/x-www-form-urlencoded
有文件:multipart/form-data
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:@"Ben" forKey:@"first_name"];
[request setPostValue:@"Copsey" forKey:@"last_name"];
[request setFile:@"/Users/ben/Desktop/ben.jpg" forKey:@"photo"];
[request addData:imageData withFileName:@"george.jpg" andContentType:@"image/jpeg" forKey:@"photos"];

 

下载:

1.创建请求队列:

首先,创建网络请求队列,如下:
ASINetworkQueue   *que = [[ASINetworkQueue alloc] init];
self.netWorkQueue = que;
[que release];

[self.netWorkQueue reset];
[self.netWorkQueue setShowAccurateProgress:YES];
[self.netWorkQueue go];

2.设置存放路径

//初始化Documents路径
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
//初始化临时文件路径
NSString *folderPath = [path stringByAppendingPathComponent:@"temp"];
//创建文件管理器
NSFileManager *fileManager = [NSFileManager defaultManager];
//判断temp文件夹是否存在
BOOL fileExists = [fileManager fileExistsAtPath:folderPath];

if (!fileExists) {//如果不存在说创建,因为下载时,不会自动创建文件夹
[fileManager createDirectoryAtPath:folderPath 
           withIntermediateDirectories:YES 
                            attributes:nil
                                 error:nil];

3.发送请求

这里对下面几个对象说明一下:CustomCell是我自定义的cell,cell上面有下载和暂停两个按钮,其tag值为cell所在的行,因此这里的[sendertag]为下载按钮的tag值,self.downloadArray为array数组对象,存放要下载的资源字典信息,在该字典中有一键为URL,它对应的值就是我们下载链接。
这些东西,根据自己的实际需要改动一下即可使用

CustomCell *cell = (CustomCell *)[self.myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:[sender tag] inSection:0]];
NSString  *filePath = [[self.downloadArray objectAtIndex:[sender tag]] objectForKey:@"URL"];
NSLog(@"filePath=%@",filePath);
//初始下载路径
NSURL *url = [NSURL URLWithString:filePath];
//设置下载路径
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:url];
//设置ASIHTTPRequest代理
request.delegate = self;
//初始化保存ZIP文件路径
NSString *savePath = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"book_%d.zip",[sender tag]]];
//初始化临时文件路径
NSString *tempPath = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"temp/book_%d.zip.temp",[sender tag]]];
//设置文件保存路径
[request setDownloadDestinationPath:savePath];
//设置临时文件路径
[request setTemporaryFileDownloadPath:tempPath];

//设置进度条的代理,
[request setDownloadProgressDelegate:cell];
//设置是是否支持断点下载
[request setAllowResumeForFileDownloads:YES];
//设置基本信息
[request setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:[sender tag]],@"bookID",nil]];

NSLog(@"UserInfo=%@",request.userInfo);
//添加到ASINetworkQueue队列去下载
[self.netWorkQueue addOperation:request];
//收回request
[request release];

 

/ASIHTTPRequestDelegate,下载之前获取信息的方法,主要获取下载内容的大小,可以显示下载进度多少字节
- (void)request:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)responseHeaders {
 NSLog(@"didReceiveResponseHeaders-%@",[responseHeaders valueForKey:@"Content-Length"]);
 
    NSLog(@"contentlength=%f",request.contentLength/1024.0/1024.0);
    int bookid = [[request.userInfo objectForKey:@"bookID"] intValue];
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    float tempConLen = [[userDefaults objectForKey:[NSString stringWithFormat:@"book_%d_contentLength",bookid]] floatValue];
    NSLog(@"tempConLen=%f",tempConLen);
    //如果没有保存,则持久化他的内容大小
    if (tempConLen == 0 ) {//如果没有保存,则持久化他的内容大小
        [userDefaults setObject:[NSNumber numberWithFloat:request.contentLength/1024.0/1024.0] forKey:[NSString stringWithFormat:@"book_%d_contentLength",bookid]];
    }
 
}

 

//ASIHTTPRequestDelegate,下载完成时,执行的方法
- (void)requestFinished:(ASIHTTPRequest *)request {

int bookid = [[request.userInfo objectForKey:@"bookID"] intValue];
CustomCell *cell = (CustomCell *)[self.myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:bookid inSection:0]];
cell.downloadCompleteStatus = YES;
cell.progressView.progress = 0.0; 
}

作者:风继续吹0
链接:https://www.jianshu.com/p/c24cad69f89c
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

AF2.0

1.实现请求的方法

 

2. 实现

NSURLConnection (iOS 6 & 7)

AFURLConnectionOperation – 它继承于 NSOperation,负责管理 NSURLConnection,实现它的 delegate 方法。
AFHTTPRequestOperation – 它继承于 AFURLConnectionOperation,专门用于创建 HTTP 请求。2.0 的主要区别就是可以直接使用它,而不用再继承它,原因将会在下面的 Serialization 处解释。
AFHTTPRequestOperationManager – 封装 HTTP 请求的常见方式,GET / POST / PUT / DELETE / PATCH……
NSURLSession (iOS 7) AFURLSessionManager – 创建和管理 NSURLSession 对象,以及此对象的数据和下载/上传等任务,实现对象和任务的代理方法。NSURLSession 的 API 本身有一些局限,AFURLSessionManager 能使其变得更好用。 AFHTTPSessionManager – 它继承于 AFURLSessionManager,封装了 HTTP 请求的常见方式,GET / POST / PUT / DELETE / PATCH…… 总的来说:为了支持最新的 NSURLSession 接口,同时兼容旧的 NSURLConnection,2.0 的核心组件将“网络请求”和“任务处理”分离。 AFHTTPRequestOperationManager 和 AFHTTPSessionManager 提供相似的功能,切换很方便,所以从 iOS 6 移植到 iOS 7 会很容易。之前绑在 AFHTTPClient 里的 serialization、security 和 reachability 模型都被分离了出来,基于 NSURLSession 和 NSURLConnection 的 API 都可复用这些模型。

 

2.AFURLRequestserializtion(序列化)

概述:

RequestSerilization 是AFNetwroking中对网络请求中request这个概率的封装。它的原型其实是NSURLRequest,将NSURLRequest进行第二次封装,将许多诸如请求头,请求参数格式化, multipar/form data文件上传等进行了简化处理。
总结来说,使用AFURLRequestSerializer有以下几个优点:
1、自动处理的请求参数转义,以及对不同请求方式自动对请求参数进行格式化。
2、实现了multipart/form-data方式的请求。
3、自动处理了User-Agent,Language等请求头。

使用:

AFURLRequestSerializtion在AF框架中是封装请求这一部分对象的,作为AFHTTPSessionManaager的一个属性被使用。
如:

 ///    request data parse
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.requestSerializer.timeoutInterval = 30.f;
如果上传时使用的是json格式数据,那么使用AFJSONRequestSerializer:

 manager.requestSerializer = [AFJSONRequestSerializer serializer];
原来存在于NSURLRequest对象的属性,都可以该对象使用如:

[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"accept"];

 

序列化(Serialization)

2.0 架构的一个突破就是,请求和解析的可序列化。序列化的灵活性允许在网络层添加更多的商业逻辑,自定义更方便。<AFURLRequestSerializer><AFURLResponseSerializer> 这两个协议,让你在 1.0 中的一些抱怨不复存在。

 

3.安全

安全

AFNetworking 支持 SSL pinning。这对涉及敏感数据的 App 很重要。

AFSecurityPolicy – 这个类通过特定证书和公共密钥评估链接的安全性和可信任度。在你的 App bundle 中添加服务器证书有助于防止“中间人攻击”。

4.

可达性(Reachability)

另一个从 AFHTTPClient 中分离的功能是网络的可达性。现在你可以单独使用它,或者作为 AFHTTPRequestOperationManager / AFHTTPSessionManager 的一个属性。

AFNetworkReachabilityManager – 负责监控当前的网络可达性,当网络的可达性发生改变时,提供相应的 callback 和通知。
UIKit 扩展

2.0 的中所有 UIKit 扩展都被分离出来并进行了增强。

AFNetworkActivityIndicatorManager: 新增自动开始或结束状态栏上的网络指示器。
UIImageView+AFNetworking: 新增显示图片前剪裁或者加滤镜的功能。
UIButton+AFNetworking (新增): 类似 UIImageView+AFNetworking,按钮的背景图从线上下载。
UIActivityIndicatorView+AFNetworking (新增): 根据网络请求的状态自动开始或结束。
UIProgressView+AFNetworking (新增): 自动跟踪某个请求的上传下载的进度。
UIWebView+AFNetworking (新增): 支持网络请求的进度和内容转换。

5.集成

在 CocoaPods 中使用 AFNetworking 2.0:

platform :ios, '7.0'
pod "AFNetworking", "2.0.0"

 

6.代码

AFHTTPRequestOperation *request = [[AFHTTPRequestOperation alloc] initWithRequest:urlrequest];

request.responseSerializer = [AFJSONResponseSerializer serializer];
//设置回调的queue,默认是在mainQueue执行block回调
request.completionQueue = your_request_operation_completion_queue();
[request setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
         //设置了'completionQueue'后,就可以在这里处理复杂的逻辑
         //不用担心block住了主线程
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {

 }];
 [request start];

 

posted on 2017-12-24 01:54  古成风云  阅读(272)  评论(0编辑  收藏  举报

导航