美文网首页
iOS网络1——NSURLConnection使用详解

iOS网络1——NSURLConnection使用详解

作者: 九洲仙人 | 来源:发表于2017-03-24 22:42 被阅读94次

    阅读目录

    一、整体介绍

    二、使用的一般步骤

    三、举例说明

    四、NSData 数据的反序列化

    注意事项:

    iOS9访问http网页

    原文在此

    一、整体介绍

    NSURLConnection是苹果提供的原生网络访问类,但是苹果很快会将其废弃,且由NSURLSession(iOS7以后)来替代。目前使用最广泛的第三方网络框架AFNetworking最新版本已弃用了NSURLConnection,那我们学习它还有什么用呢?

    首先,苹果弃用它还是需要时间的,最起码到iOS10之后;

    现在还有一些老项目会使用NSURLConnection,特别是2013年之前的项目,用户量基础还是很大的;

    另外,不得不承认,有些公司还在用类似ASI这些经典的网络框架,所以还是很有必要学习NSURLConnection的。

    二、使用的一般步骤

    1NSURL:请求地址,定义一个网络资源路径:

    NSURL*url = [NSURLURLWithString:@"协议://主机地址/路径?参数&参数"];

    解释如下:

    协议:不同的协议,代表着不同的资源查找方式、资源传输方式,比如常用的http,ftp等

    主机地址:存放资源的主机的IP地址(域名)

    路径:资源在主机中的具体位置

    参数:参数可有可无,也可以多个。如果带参数的话,用“?”号后面接参数,多个参数的话之间用&隔开

    2NSURLRequest:请求,根据前面的NSURL建立一个请求:

    NSURLRequest*request = [NSURLRequestrequestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicytimeoutInterval:15.0];

    参数解释如下:

    url:资源路径

    cachePolicy:缓存策略(无论使用哪种缓存策略,都会在本地缓存数据),类型为美剧类型,取值如下:

    NSURLRequestUseProtocolCachePolicy = 0 //默认的缓存策略,使用协议的缓存策略

    NSURLRequestReloadIgnoringLocalCacheData = 1 //每次都从网络加载

    NSURLRequestReturnCacheDataElseLoad = 2 //返回缓存否则加载,很少使用

    NSURLRequestReturnCacheDataDontLoad = 3 //只返回缓存,没有也不加载,很少使用

    timeoutInterval:超时时长,默认60s

    另外,还可以设置其它一些信息,比如请求头,请求体等等,如下:

    注意,下面的request应为NSMutableURLRequest,即可变类型

    // 告诉服务器数据为json类型[request setValue:@"application/json"forHTTPHeaderField:@"Content-Type"];// 设置请求体(json类型)NSData*jsonData = [NSJSONSerializationdataWithJSONObject:@{@"userid":@"123456"} options:NSJSONWritingPrettyPrintederror:nil];request.HTTPBody = jsonData;

    3 发送请求:

    NSURLConnection默认的请求类型为GET,下面分异步和同步两种介绍

    异步请求

    [NSURLConnectionsendAsynchronousRequest:request queue:[[NSOperationQueuealloc] init] completionHandler:^(NSURLResponse*response,NSData*data,NSError*connectionError) {// 有的时候,服务器访问正常,但是会没有数据!// 以下的 if 是比较标准的错误 处理代码!if(connectionError !=nil|| data ==nil) {//给用户的提示信息NSLog(@"网络不给力");return;    }}];

    参数说明如下:

    completionHandler:请求响应后(或者请求超时)执行的代码,queue为代码添加到的队列,即block执行的线程

    NSURLResponse 为服务器的响应,真实类型为NSHTTPURLResponse,通常只在“下载”功能时,才会使用;下面是协议头的参数:

    URL:响应的URL,有的时候,访问一个URL地址,服务器可能会出现重定向,会定位到新的地址!

    MIMEType(Content-Type):服务器告诉客户端,可以用什么软件打开二进制数据!网络之所以丰富多采,是因为有丰富的客户端软件!栗子:windows上提示安装 Flash 插件

    expectedContentLength:预期的内容长度,要下载的文件长度,下载文件时非常有用

    suggestedFilename:"建议"的文件名,方便用户直接保存,很多时候,用户并不关心要保存成什么名字!

    textEncodingName:文本的编码名称 @"UTF8",大多数都是 UTF8

    statusCode:状态码,在做下载操作的时候,需要判断一下

    allHeaderFields:所有的响应头字典时候,用户并不关心要保存成什么名字!

    NSData 服务器返回的数据,例如json、xml(现在用的少)

    NSError 网络访问错误码

    注意,block的执行线程为queue,如果block涉及到UI操作,则必须回到主线程:[NSOperationQueue mainQueue]

    发送同步请求

    // 同步请求,代码会阻塞在这里一直等待服务器返回,如果data为nil则请求失败,当获取少量数据时可以使用此方法。// request参数同上。NSData*data = [NSURLConnectionsendSynchronousRequest:request returningResponse:nilerror:nil];

    三、举例说明

    分三个部分:

    网络请求(json、xml数据)

    文件下载

    文件上传,这里例子不再给出,请参考这里:http://www.cnblogs.com/mddblog/p/5215453.html

    1 一般的网络请求

    /// 两种请求方式typedefenum{    MethodGET,    MethodPOST}Method;/// 请求成功后,直接将jsonData转为jsonDict+ (void)connectionRequestWithMethod:(Method)method  URLString:(NSString*)URLString parameters:(NSDictionary*)dict success:(void(^)(idJSON))success fail:(void(^)(NSError*error))fail {// 简单的转码,如果参数带有?&特殊字符,下面方法不适合URLString = [URLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];// 1.创建请求NSURL*url = [NSURLURLWithString:URLString];NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:url];// 请求方式,默认为GETif(method == MethodPOST) {        request.HTTPMethod =@"POST";    }// 根据需要设置[request setValue:@"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"forHTTPHeaderField:@"Accept"];// 2.设置请求头 Content-Type  返回格式[request setValue:@"application/json"forHTTPHeaderField:@"Content-Type"];// 3.设置请求体 NSDictionary --> NSDataif(dict !=nil) {NSData*data = [NSJSONSerializationdataWithJSONObject:dict options:NSJSONWritingPrettyPrintederror:nil];        request.HTTPBody = data;    }// 4.发送请求[NSURLConnectionsendAsynchronousRequest:request queue:[NSOperationQueuemainQueue] completionHandler:^(NSURLResponse*response,NSData*data,NSError*connectionError) {if((data !=nil) && (connectionError ==nil)) {NSDictionary*jsonDict = [NSJSONSerializationJSONObjectWithData:data options:NSJSONReadingMutableContainerserror:nil];if(success) {                success(jsonDict);            }        }else{if(fail) {                fail(connectionError);            }        }            }];}

    2 文件下载

    下面举一个实现断点续传的网络下载,要实现断点续传,还需要用到NSURLConnectionDataDelegate代理,NSFileHandle文件操作或者数据流的形式(NSOutputStream),这里以NSFileHandle为例,访问的服务器采用Apache搭建的本地服务器,具体见代码:

    @interfaceViewController() /// 文件下载完毕之后,在本地保存的路径@property(nonatomic,copy)NSString*filePath;/// 需要下载的文件的总大小!@property(nonatomic,assign)longlongserverFileLength;/// 当前已经下载长度@property(nonatomic,assign)longlonglocalFileLength;@end@implementationViewController- (void)viewDidLoad {    [superviewDidLoad];    }/// 点击屏幕事件- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event {    [selfdownloadFile];}/// 下载文件- (void)downloadFile {// 文件存储路径self.filePath =@"/Users/username/Desktop//陶喆 - 爱很简单.mp3";// 要下载的网络文件,采用Apache搭建的本地服务器NSString*urlStr =@"http://localhost/陶喆 - 爱很简单.mp3";// 简单的转码,如果参数带有?&特殊字符,下面方法不适合urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];NSURL*url = [NSURLURLWithString:urlStr];NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:url];// 设置请求方法为 HEAD 方法,这里只是头,数据量少,用同步请求也可以,不过最好还是用异步请求request.HTTPMethod =@"HEAD";// 无论是否会引起循环引用,block里面都使用弱引用__weaktypeof(self) weakSelf =self;    [NSURLConnectionsendAsynchronousRequest:request queue:[[NSOperationQueuealloc] init] completionHandler:^(NSURLResponse* _Nullable response,NSData* _Nullable data,NSError* _Nullable connectionError) {// 出错则返回if(connectionError !=nil) {NSLog(@"connectionError = %@",connectionError);return;        }// 记录需要下载的文件总长度longlongserverFileLength = response.expectedContentLength;        weakSelf.serverFileLength = serverFileLength;// 初始化已下载文件大小weakSelf.localFileLength =0;NSDictionary*dict = [[NSFileManagerdefaultManager] attributesOfItemAtPath:weakSelf.filePath error:NULL];longlonglocalFileLength = [dict[NSFileSize] longLongValue];// 如果没有本地文件,直接下载!if(!localFileLength) {// 下载新文件[weakSelf getFileWithUrlString:urlStr];        }// 如果已下载的大小,大于服务器文件大小,肯定出错了,删除文件并从新下载if(localFileLength > serverFileLength) {// 删除文件 remove[[NSFileManagerdefaultManager]  removeItemAtPath:self.filePath error:NULL];// 下载新文件[selfgetFileWithUrlString:urlStr];                    }elseif(localFileLength == serverFileLength) {NSLog(@"文件已经下载完毕");        }elseif(localFileLength && localFileLength < serverFileLength) {// 文件下载了一半,则使用断点续传self.localFileLength = localFileLength;                        [selfgetFileWithUrlString:urlStr WithStartSize:localFileLength endSize:serverFileLength-1];        }    }];    }/// 重新开始下载文件(代理方法监听下载过程)-(void)getFileWithUrlString:(NSString*)urlString{NSURL*url = [NSURLURLWithString:urlString];// 默认就是 GET 请求NSURLRequest*request = [NSURLRequestrequestWithURL:url];// 开始请求,并设置代理为selfNSURLConnection*conn = [[NSURLConnectionalloc] initWithRequest:request delegate:self];// 设置代理的执行线程,一般不在主线程[conn setDelegateQueue:[[NSOperationQueuealloc] init]];// NSUrlConnection 的代理方法是一个特殊的事件源!// 开启运行循环!CFRunLoopRun();}/* 断点续传(代理方法监听下载过程)

    * startSize:本次断点续传开始的位置

    * endSize:本地断点续传结束的位置

    */-(void)getFileWithUrlString:(NSString*)urlString WithStartSize:(longlong)startSize endSize:(longlong)endSize{// 1. 创建请求!NSURL*url = [NSURLURLWithString:urlString];// 默认就是 GET 请求NSMutableURLRequest*request = [NSMutableURLRequestrequestWithURL:url];// 设置断点续传信息NSString*range = [NSStringstringWithFormat:@"Bytes=%lld-%lld",startSize,endSize];    [request setValue:range forHTTPHeaderField:@"Range"];// NSUrlConnection 下载过程!NSURLConnection*conn = [[NSURLConnectionalloc] initWithRequest:request delegate:self];// 设置代理的执行线程// 传一个非主队列![conn setDelegateQueue:[[NSOperationQueuealloc] init]];// NSUrlConnection 的代理方法是一个特殊的事件源!// 开启运行循环!CFRunLoopRun();}#pragma mark - NSURLConnectionDataDelegate/// 1. 接收到服务器响应- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {// 服务器响应// response.expectedContentLength的大小是要下载文件的大小,而不是文件的总大小。比如请求头设置了Range[requestM setValue:rangeStr forHTTPHeaderField:@"Range"];则返回的大小为range范围内的大小}/// 2. 接收到数据- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {//data当前接收到的网络数据;// 如果这个文件不存在,响应的文件句柄就不会创建!NSFileHandle*fileHandle = [NSFileHandlefileHandleForWritingAtPath:self.filePath];// 在这个路径下已经有文件了!if(fileHandle) {// 将文件句柄移动到文件的末尾[fileHandle seekToEndOfFile];// 写入文件的意思(会将data写入到文件句柄所操纵的文件!)[fileHandle writeData:data];                [fileHandle closeFile];            }else{// 第一次调用这个方法的时候,在本地还没有文件路径(没有这个文件)![data writeToFile:self.filePath atomically:YES];    }}/// 3. 接收完成- (void)connectionDidFinishLoading:(NSURLConnection*)connection {NSLog(@"下载完成");}/// 4. 网络错误- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error {NSLog(@"下载错误 %@", error);}@end

    四、NSData 数据的反序列化

    服务器返回的NSData数据类型,事先已经知道,因此可以直接返序列化为实际的类型,例如:json(字典或数组).plist(字典或数组)textxml等:

    加载数据

    // 加载本地NSString*path = [[NSBundlemainBundle] pathForResource:@"文件名 "ofType:nil];NSData*jsonData = [NSDatadataWithContentsOfFile:path];// 从网络直接加载NSURL*url = [[NSBundlemainBundle] URLForResource:@"topic_news.json"withExtension:nil];NSData*data = [NSDatadataWithContentsOfURL:url];

    .plist反序列化

    这种很少使用,只是苹果自己的格式

    // 关于选型参数// NSPropertyListImmutable = 0,不可变// NSPropertyListMutableContainers = 1 << 0,容器可变// NSPropertyListMutableContainersAndLeaves = 1 << 1,容器和叶子可变// 通常后续直接做字典转模型,不需要关心是否可变,所以一般直接赋值为0idresult = [NSPropertyListSerializationpropertyListWithData:data options:0format:NULLerror:NULL];

    关于json数据

    json数据的本质是字符串,根格式只有两种可能:数组或字典。

    反序列化:从服务器接收到的二进制数据 转换成 字典或者数组

    // 假设json数据位:[{键值对},{键值对}],则可以将数据转化为字典或数组NSArray*dictArray = [NSJSONSerializationJSONObjectWithData:jsonData options:NSJSONReadingMutableContainerserror:nil];

    序列化:将字典或者数组 转换成 二进制数据,准备发送给服务器

    // obj为字典或数组NSData*data = [NSJSONSerializationdataWithJSONObject:obj options:0error:NULL];// 转化前可以判断是否可以序列化:+ (BOOL)isValidJSONObject:(id)obj;

    一个对象能够被转换成 JSON 必须符合以下条件:

    顶级节点,必须是一个 NSArray or NSDictionary

    所有的对象必须是 NSString, NSNumber, NSArray, NSDictionary, or NSNull

    所有字典的 key 都必须是 NSString

    NSNumber 不能为空或者无穷大

    注意事项:

    请求的缓存策略使用 NSURLRequestReloadIgnoringCacheData,忽略本地缓存

    服务器响应结束后,要记录 Etag,服务器内容和本地缓存对比是否变化的重要依据!

    在发送请求时,设置 If-None-Match,并且传入 etag

    连接结束后,要判断响应头的状态码,如果是 304,说明本地缓存内容没有发生变化,此时可以使用本地缓存来加载数据,如果缓存文件被意外删除,程序依然运行但会报错,加载数据也失败:NSCachedURLResponse *cachedURLRes = [[NSURLCache sharedURLCache] cachedResponseForRequest:requestM];//cachedURLRes为nil

    GET 缓存的数据会保存在 Cache 目录中 /bundleId 下,Cache.db 中

    iOS9访问http网页

    在Info.plist中添加NSAppTransportSecurity类型Dictionary;

    在NSAppTransportSecurity下添加NSAllowsArbitraryLoads类型Boolean,值设为YES

    相关文章

      网友评论

          本文标题:iOS网络1——NSURLConnection使用详解

          本文链接:https://www.haomeiwen.com/subject/mbjqottx.html