多线程网络05
2019-12-15 本文已影响0人
努力爬行中的蜗牛
1 JSON解析代码
1.1 JSON -> OC
- (void)jsonWithObj {
//NSString *str = @"{\"error\":\"用户名不存在\"}"; //字典-NSJSONReadingMutableContainers
//NSString *str = @"[\"error\",\"用户名不存在\"]"; //数组-NSJSONReadingMutableContainers
//NSString *str = @"\"json string\""; //字符串-NSJSONReadingAllowFragments
//NSString *str = @"false"; //数字 NSJSONReadingAllowFragments
/*
NSJSONReadingMutableContainers = (1UL << 0), 可变字典和数组
NSJSONReadingMutableLeaves = (1UL << 1), 内部所有字符串都是可变的 ios7之后有问题,一般不用
NSJSONReadingFragmentsAllowed = (1UL << 2), 既不是数组也不是字典,则必须使用该枚举值
*/
NSString *str = @"null";
id obj = [NSJSONSerialization JSONObjectWithData:[str dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@---%@",[obj class],obj);
}
1.2 OC -> JSON
- (void)objToJson {
NSDictionary *dict = @{
@"name":@"xiaoming",
@"age":@"2"
};
NSArray *arr = @[@"name",@"age"];
NSString *str = @"hello world";
// 并不是所有的对象都可以转换为JSON
/*
- 最外层必须是 NSArray or NSDictionary
- 所有的元素是 NSString, NSNumber, NSArray, NSDictionary, or NSNull
- 字典中所有的key必须是 NSStrings
- NSNumbers 不能无穷大
*/
BOOL isValid = [NSJSONSerialization isValidJSONObject:str];
NSLog(@"%d",isValid);
if (!isValid) {
return;
}
/*
参数1:要转换的OC对象
参数2:选项NSJSONWritingPrettyPrinted 美化 排版
*/
NSData *data = [NSJSONSerialization dataWithJSONObject:str options:NSJSONWritingPrettyPrinted error:nil];
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
2 MJExtension
2.1 相关框架
- Mantal 需要继承自MIModel
- JSONModel 需要继承自JSONModel
- MJExtension 不需要继承,无代码侵入性
2.2 设计和选择框架时需要注意的问题
- 侵入性
- 易用性,是否容易上手
- 扩展性,很容易给这个框架增加新的内容
2.3 MJExtension是一套字典和模型之间互相转换的超级轻量框架
3 XML简单介绍
3.1 XML语法 - 元素(Element)
- 一个元素包括了开始标签和结束标签
- 拥有内容的元素:
<video>小黄人</video>
- 没有内容的元素:
<video></video>
- 没有内容的元素简写:
<video/>
- 一个元素可以嵌套若干个子元素(不能出现交叉嵌套)
- 规范的XML文档最多只有1个根元素,其他元素都是根元素的子孙元素
3.2 XML语法 - 元素的注意
- XML中的所有空格和换行,都会当做具体内容处理
- 下面两个元素的内容是不一样的
// 第一个
<video>小黄人<video/>
// 第二个
<video>
小黄人
<video/>
3.3 XML语法 - 属性(Attribute)
- 一个元素可以拥有多个属性
<video name="小黄人 第01部" length="30" />
video元素拥有 name
和length
两个属性
属性值必须用双引号"" 或者 单引号 '' 扩住
- 实际上,属性表示的信息也可以用子元素来表示,不如
<video>
<name>小黄人 第01部</name>
<length>30</length>
</video>
3.4 XML解析
- 想要从XML中提取有用的信息,必须得学会解析XML
1)提取name
元素里面的内容
<name>小黄人 第01部</name>
2)提取video
元素中name
和length
属性的值
<video name="小黄人 第01部" length="30" /> - XML的解析方式有2种
1)DOM:一次性将整个XML文档加载进内存,比较适合解析小文件
2)SAX:从根元素开始,按顺序一个元素一个元素往下解析,比较适合解析大文件
3.5 iOS中的XML解析
- 在iOS中,解析XML的手段很多
1)苹果原生:NSXMLParser:SAX方式解析,使用简单
2)第三方框架
libxml2:纯c语言,默认包含在iOS SDK中,同时支持DOM和SAX方式解析
GDataXML:DOM方式解析,由Google开发,基于libxml2 - XML解析方式的选择建议
1)大文件:NSXMLParser、libxml2
2)小文件:GDataXML、NSXMLParser、libxml2
4 NSXMLParser解析XML:SAX
- (void)xml {
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Provinces.xml" ofType:nil]];
//1 创建解析器
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
//2 设置代理
parser.delegate = self;
//3 解析文件
[parser parse]; // 阻塞
}
#pragma mark - NSXMLParserDelegate
//1 开始解析xml文档
- (void)parserDidStartDocument:(NSXMLParser *)parser {
NSLog(@"%s",__func__);
}
//2 开始解析xml某个元素
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary<NSString *,NSString *> *)attributeDict {
if ([elementName isEqualToString:@"Provinces"]) {
return;
}
NSLog(@"%@---%@",elementName,attributeDict);
}
//3 xml某个元素解析完毕
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
NSLog(@"%@元素解析完毕",elementName);
}
//4 xml文档解析完毕
- (void)parserDidEndDocument:(NSXMLParser *)parser {
NSLog(@"%s",__func__);
}
5 GDataXML解析XML:DOM
5.1 GDataXML配置
- GDataXML基于libxml2库,得做以下配置
导入libxml2
库 - 设置
libxml2
的头文件搜索路径(为了能找到libxml2库的所有头文件)
在Head Search Path中加入/usr/include/libxml2 - 设置链接参数(自动链接libxml2库)
在Other Linker Flags中加入-lmxl2 - mrc文件编译方式
-fno-objc-arc
- (void)xml {
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Provinces.xml" ofType:nil]];
//1 加载xml文档
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:data options:kNilOptions error:nil];
//2 拿到根元素,得到元素内部所有名称为video的子孙元素
NSArray *elements = [doc.rootElement elementsForName:@"Province"];
//3 遍历
for (GDataXMLElement *element in elements) {
ProviceModel *model = [[ProviceModel alloc] init];
model.provinceName = [element attributeForName:@"ProvinceName"].stringValue;
[self.dataArray addObject:model];
}
NSLog(@"%zd",self.dataArray.count);
}
- (NSMutableArray *)dataArray {
if (!_dataArray) {
_dataArray = [NSMutableArray array];
}
return _dataArray;
}
6 多值参数和中文输出
6.1 多值参数
htpp://120.25.227.189:3244/weather?place=Sichuan&place=Beijing"
6.2 中文输出
#import <Foundation/Foundation.h>
@implementation NSDictionary (Log)
//重写系统的方法控制输出
-(NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
// return @"你大爷是你大姐";
NSMutableString *string = [NSMutableString string];
//{}
[string appendString:@"{"];
//拼接key--value
[self enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
[string appendFormat:@"%@:",key];
[string appendFormat:@"%@,",obj];
}];
[string appendString:@"}"];
//删除逗号
//从后往前搜索 得到的是搜索到的第一个符号的位置
NSRange range = [string rangeOfString:@"," options:NSBackwardsSearch];
if (range.location != NSNotFound) {
[string deleteCharactersInRange:range];
}
return string;
}
@end
@implementation NSArray (Log)
//重写系统的方法控制输出
-(NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level
{
// return @"你大爷是你大姐";
NSMutableString *string = [NSMutableString string];
//{}
[string appendString:@"["];
//拼接obj
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[string appendFormat:@"%@,\n",obj];
}];
[string appendString:@"]"];
//删除逗号
//从后往前搜索 得到的是搜索到的第一个符号的位置
NSRange range = [string rangeOfString:@"," options:NSBackwardsSearch];
if (range.location != NSNotFound) {
[string deleteCharactersInRange:range];
}
return string;
}
7 NSURLConnection实现大文件下载
下载文件的时候,如果一次性下载下来,保存到内存,会导致内存飙升。
应该使用代理,将文件保存到本地。
-#import "ViewController.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progress;
@property (assign, nonatomic) NSInteger currentSize;
@property (assign, nonatomic) NSInteger totalSize;
@property (strong, nonatomic) NSURLConnection *connection;
@property (strong, nonatomic) NSFileHandle *handle;
@property (strong, nonatomic) NSString *fullPath;
@end
@implementation ViewController
- (IBAction)beginToDownLoad:(id)sender {
[self download];
}
- (IBAction)stopToDownload:(id)sender {
// 取消下载
[self.connection cancel];
NSLog(@"+++++++cancle");
}
//异步请求:代理方式
- (void)download {
//1 url地址
NSURL *url = [NSURL URLWithString:@"http://www.hitow.net/data/attachment/album/201612/10/205628nppuzll3137siny2.jpg"];
//2 请求请求对象
// 请求头不需要设置(默认请求头)
NSMutableURLRequest *requet = [NSMutableURLRequest requestWithURL:url];
// 设置请求头:告诉服务器请求一部分数据range
/*
bytes=0-100 请求前100个字节
bytes=-100 请求最后100个字节
bytes=100- 请求100之后的所有字节
*/
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[requet setValue:range forHTTPHeaderField:@"Range"];
//3 发送异步请求
self.connection = [[NSURLConnection alloc] initWithRequest:requet delegate:self];
}
#pragma mark - NSURLConnectionDataDelegate
// 1 客户端收到服务端响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(@"%s",__func__);
//expectedContentLength:当前请求的数据大小,但不一定是整个下载文件的大小
self.totalSize = response.expectedContentLength + self.currentSize;
//获取文件下载路径
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//使用服务端返回的建议文件名字
NSString *fileName = response.suggestedFilename;
//文件全路径
self.fullPath = [filePath stringByAppendingPathComponent:fileName];
//创建本地空文件
[[NSFileManager defaultManager] createFileAtPath:self.fullPath contents:nil attributes:nil];
//创建文件句柄
self.handle = [NSFileHandle fileHandleForWritingAtPath:self.fullPath];
}
//2 客户端接收到服务端数据
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//移动文件句柄到文件的末尾
[self.handle seekToEndOfFile];
//写入数据
[self.handle writeData:data];
// 获取当前下载文件的进度
self.currentSize += data.length;
CGFloat percent = (self.currentSize * 1.0)/self.totalSize;
self.progress.progress = percent;
NSLog(@"%f",percent);
}
//3 客户端数据请求失败
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"%s",__func__);
}
//4 客户度数据请求完毕
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//关闭文件句柄
[self.handle closeFile];
self.handle = nil;
NSLog(@"file download over");
}
8 输出流
输出流的方式和文件句柄类似,都是将从服务端获取的数据保存到指定的本地沙盒路径下。
#import "ViewController.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progress;
@property (assign, nonatomic) NSInteger currentSize;
@property (assign, nonatomic) NSInteger totalSize;
@property (strong, nonatomic) NSURLConnection *connection;
// 文件句柄
@property (strong, nonatomic) NSFileHandle *handle;
// 输出流
@property (strong, nonatomic) NSOutputStream *stream;
// 文件路径
@property (strong, nonatomic) NSString *fullPath;
@end
@implementation ViewController
- (IBAction)beginToDownLoad:(id)sender {
[self download];
}
- (IBAction)stopToDownload:(id)sender {
// 取消下载
[self.connection cancel];
NSLog(@"+++++++cancle");
}
//异步请求:代理方式
- (void)download {
//1 url地址
NSURL *url = [NSURL URLWithString:@"http://www.hitow.net/data/attachment/album/201612/10/205628nppuzll3137siny2.jpg"];
//2 请求请求对象
// 请求头不需要设置(默认请求头)
NSMutableURLRequest *requet = [NSMutableURLRequest requestWithURL:url];
// 设置请求头:告诉服务器请求一部分数据range
/*
bytes=0-100 请求前100个字节
bytes=-100 请求最后100个字节
bytes=100- 请求100之后的所有字节
*/
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
[requet setValue:range forHTTPHeaderField:@"Range"];
//3 发送异步请求
self.connection = [[NSURLConnection alloc] initWithRequest:requet delegate:self];
}
#pragma mark - NSURLConnectionDataDelegate
// 1 客户端收到服务端响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(@"%s",__func__);
//expectedContentLength:当前请求的数据大小,但不一定是整个下载文件的大小
self.totalSize = response.expectedContentLength + self.currentSize;
//获取文件下载路径
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//使用服务端返回的建议文件名字
NSString *fileName = response.suggestedFilename;
//文件全路径
self.fullPath = [filePath stringByAppendingPathComponent:fileName];
//创建输出流
/*
参数1:文件路径
参数2:是否追加
特殊:如果本地没有该文件,会自动创建一个空的文件
*/
self.stream = [NSOutputStream outputStreamToFileAtPath:self.fullPath append:YES];
// 打开输出流
[self.stream open];
}
//2 客户端接收到服务端数据
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//保存数据到指定的路径
[self.stream write:data.bytes maxLength:data.length];
// 获取当前下载文件的进度
self.currentSize += data.length;
CGFloat percent = (self.currentSize * 1.0)/self.totalSize;
self.progress.progress = percent;
NSLog(@"%f",percent);
}
//3 客户端数据请求失败
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"%s",__func__);
}
//4 客户度数据请求完毕
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//关闭输出流
[self.stream close];
self.stream = nil;
NSLog(@"file download over");
}
9 NSURLConnection实现文件上传
//文件上传步骤
/*
1.设置请求头
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryjv0UfA04ED44AhWx
2.按照固定的格式拼接请求体的数据
------WebKitFormBoundaryjv0UfA04ED44AhWx
Content-Disposition: form-data; name="file"; filename="Snip20160225_341.png"
Content-Type: image/png
------WebKitFormBoundaryjv0UfA04ED44AhWx
Content-Disposition: form-data; name="username"
123456
------WebKitFormBoundaryjv0UfA04ED44AhWx--
*/
//拼接请求体的数据格式
/*
拼接请求体
分隔符:----WebKitFormBoundaryjv0UfA04ED44AhWx
1)文件参数
--分隔符
Content-Disposition: form-data; name="file"; filename="Snip20160225_341.png"
Content-Type: image/png(MIMEType:大类型/小类型)
空行
文件参数
2)非文件参数
--分隔符
Content-Disposition: form-data; name="username"
空行
123456
3)结尾标识
--分隔符--
*/
#import "ViewController.h"
#define Kboundary @"----WebKitFormBoundaryjv0UfA04ED44AhWx"
#define KNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
@interface ViewController ()
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self upload];
}
-(void)upload
{
//1.确定请求路径
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/upload"];
//2.创建可变的请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//3.设置请求方法
request.HTTPMethod = @"POST";
//4.设置请求头信息
//Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryjv0UfA04ED44AhWx
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",Kboundary] forHTTPHeaderField:@"Content-Type"];
//5.拼接请求体数据
NSMutableData *fileData = [NSMutableData data];
//5.1 文件参数
/*
--分隔符
Content-Disposition: form-data; name="file"; filename="Snip20160225_341.png"
Content-Type: image/png(MIMEType:大类型/小类型)
空行
文件参数
*/
[fileData appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
//name:file 服务器规定的参数
//filename:Snip20160225_341.png 文件保存到服务器上面的名称
//Content-Type:文件的类型
[fileData appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"Snip20160225_341.png\"" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
[fileData appendData:[@"Content-Type: image/png" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
[fileData appendData:KNewLine];
UIImage *image = [UIImage imageNamed:@"Snip20160225_341"];
//UIImage --->NSData
NSData *imageData = UIImagePNGRepresentation(image);
[fileData appendData:imageData];
[fileData appendData:KNewLine];
//5.2 非文件参数
/*
--分隔符
Content-Disposition: form-data; name="username"
空行
123456
*/
[fileData appendData:[[NSString stringWithFormat:@"--%@",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
[fileData appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
[fileData appendData:KNewLine];
[fileData appendData:[@"123456" dataUsingEncoding:NSUTF8StringEncoding]];
[fileData appendData:KNewLine];
//5.3 结尾标识
/*
--分隔符--
*/
[fileData appendData:[[NSString stringWithFormat:@"--%@--",Kboundary] dataUsingEncoding:NSUTF8StringEncoding]];
//6.设置请求体
request.HTTPBody = fileData;
//7.发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
//8.解析数据
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
}