zoukankan      html  css  js  c++  java
  • 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];
  • 相关阅读:
    java 排序
    spring 收藏博文
    转载:一位软件工程师的6年总结
    网站
    jdk配置环境变量的方法
    推荐桌游
    js 猜数字游戏
    html简易计算器的前端代码
    (转载)float与double中的精度问题
    jiaxiang
  • 原文地址:https://www.cnblogs.com/guchengfengyun/p/8095484.html
Copyright © 2011-2022 走看看