网络数据请求
1. 确定地址NSURL
有两种方式可以把数据提交给服务器:GET和POST
GET和POST请求的区别
GET使用的是URL或Cookie传参,而POST将数据存放在Body中。
GET的URL会受长度的限制,而POST的数据则可以非常大。
POST比GET要安全,因为地址栏上看不见。
2. 向服务器发送请求
使用NSURLRequest可以向服务器发送简单的请求,而使用NSURLMutableRequest,则可以向服务器发送一些复杂的可变请求,因为NSURLMutableRequest可以添加请求头和请求参数
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:@"http://baidu.com"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//请求类型
request.HTTPMethod = @"POST";
//请求时长
request.timeoutInterval = 2.0;
//请求体类型转换
NSURL *bodyUrl = [NSURL URLWithString:@"username=11111&password=2222"];
//添加请求体
request.HTTPBody = [NSData dataWithContentsOfURL:bodyUrl];
}
3. 建立并启动连接NSURLSession
当完成request请求之后,可以用NSURLSession于Web建立联系,从而获取数据。NSURLSession的两个核心概念是:
会话:用来处理会话任务,开发者可以配置session的缓存,协议,cookie,以及证书策略
会话任务:负责处理数据的加载以及文件和数据在客户端于服务器端之间的上传和下载。
- (void)viewDidLoad {
[super viewDidLoad];
//会话的工作模式
/*
defaultSessionConfiguration;
默认会话模式,使用的是基于磁盘缓存的持久化策略,使用用户keychain中保存的证书进行认证授权。
ephemeralSessionConfiguration;
瞬时会话模式,该模式不使用磁盘保存任何数据。所有和会话相关的caches,证书,cookies都被保存在RAM中,因此当程序会话无效,这些缓存的数据就会被自动的清空。
backgroundSessionConfigurationWithIdentifier:(NSString *)identifier;
后台会话模式,该模式在后台完成长传和下载,在创建configuration对象的时候需要提供一个NSString类型的ID用来标识工作完成的后台会话
*/
//实例化一个会话的工作模式
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
/*
获取NSURLSession类对象的三中方法:
+ (NSURLSession *)sharedSession;
使用静态的sharedSession方法,该类使用共享的会话,该会话使用全局的cache,cookie和证书。
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
该类可以创建对应配置的会话,和NSURLSessionConfiguration配合使用
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;
创建一个新的会话并指定其会话类型,该方法指定了session的委托和委托所处的队列。当不再需要连接时可以调用session的invalidateAndCancel方法直接关闭,或者调用finishTasksAndInvalidate等待当前Task结束后关闭。这时Delegate会收到URLSession:didBecomeInvalidWithError:这个事件。Delegate收到这个事件之后会被解引用
*/
//实例化一个会话
NSURLSession *session2 = [NSURLSession sessionWithConfiguration:configuration];
/*
NSURLSessionTask类
NSURLSessionTask是一个抽象子类,它有三个子类: NSURLSessionDataTask,
获取数据(JSON,XML)
NSURLSessionUploadTask
上传文件
NSURLSessionDownloadTask
下载文件
*/
//创建一个获取数据的会话任务
NSURL *url = [NSURL URLWithString:@"http://......"];
NSURLSessionDataTask *dataTask = [session2 dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil)
{
if (data != nil)
{
NSLog(@"数据获取成功");
}
}
} ];
//启动会话任务
[dataTask resume];
}
网络数据解析
常用的2种网络数据交换格式:
JSON:轻量级的数据交换格式,是基于JavaScript的一个子集,但易读性差,编码手写难度大
XML:可扩展标记语言,易读性高,编码手写难度小,但数据量大。
JSON
JSON主要由两种数据结构:有key-value对组成的数据结构(在Objective-C中是一种NSDictionary对象)和有序集合(在Object-C中是NSArray)。这两种数据结构在不同的语言中都有对象的实现
使用NSJSONSerialization处理JSON数据
[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
注意:
NSJSONSerialization并不能对将任意对象都转换成JSON数据,顶级对象里能包含NSArray和NSDictionaty,集合中包含的对象只能是:NSString、NSNumber、NSArray、NSDictionary、或NSNull对象,NSDictionary的key只能是NSString。
XML
XML文档解析的两套规范(标准):DOM和SAX
DOM是将该文档转换成常驻内存的树状结构,程序代码通过节点与节点之间的父子关系来获取每个节点所包含的数据。DOM标准简单易用,但它需要一次性的读取整个XML文档,并在程序运行期间常占内存,导致系统开销过大。
SAX是采用事件机制的方式来解析XML文档的,使用XML解析器对XML文档进行解析时,会触发一系列事件,应用程序通过这些事件的处理方法实现对XML文档的访问。SAX解析方式占用内存极小,速度更快。
XML的解析方式:
NSXMLParser:只是iOS SDK自带的解析器,是基于SAX解析的,用Objective-C实现的。但他的性能不太好。
Libxml2:这是iOS SDK自带的解析器,他是一套开源的解析器,是采用C语言实现的,因此用起来不太方便。它的功能非常强大,可同时支持DOM和SAX解析。
GDateXML:只是第三方提供的基于DOM的XML解析,它是Libxml的包装,支持读取和修改XML文档。
使用NSXMLParser解析XML数据
@interface ViewController ()
//一个可变数组用来存放解析的对象
@property (nonatomic, strong)NSMutableArray *dataArray;
//全局的字符串,记录每一个元素的完整内容
@property (strong, nonatomic) NSMutableString *elementString;
//全局的currentObject对象,记录当前正在解析的元素(该对象是开发者自定义的对象)
@property (strong, nonatomic) object *currentObject;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//创建NSXMLParser对象
NSXMLParser *parser = [[NSXMLParser alloc] init];
//为NSXMLParer对象指定delegate对象
parser.delegate = self;
//调用NSXMLParser对象的parse方法开始解析
[parser parse];
}
// 1. 解析文档
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
NSLog(@"开始解析");
// 懒加载实例化数据
if (self.dataArray == nil) {
self.dataArray = [NSMutableArray array];
} else {
[self.dataArray removeAllObjects];
}
// 中间字符串
if (self.elementString == nil) {
self.elementString = [NSMutableString string];
}
}
// 在整个XML文件解析完成之前,2、3、4方法会不断被循环调用
// 2. 开始解析一个元素,新的节点开始了
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(@"开始解析元素 %@ %@", elementName, attributeDict);
if ([elementName isEqualToString:@"video"]) {
// 1. 实例化currentObject
self.currentObject = [[object alloc]init];
}
// 需要清空中转的字符串
[self.elementString setString:@""];
}
// 3. 接收元素的数据(发现字符,因为元素内容过大,此方法可能会被重复调用,需要拼接数据)
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
NSLog(@"发现字符 %@", string);
// 拼接字符串
[self.elementString appendString:string];
}
// 4. 结束解析一个元素
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
NSLog(@"完成解析元素 %@", elementName);
// 获得第3步拼接出来的字符串
NSString *result = [NSString stringWithString:self.elementString];
if ([elementName isEqualToString:@"对象的属性"]) {
//在此处将result赋值给currentObject的某个属性
} else if ([elementName isEqualToString:@"对象的属性"]) {
//在此处将result赋值给currentObject的某个属性
} else if ([elementName isEqualToString:@"对象的属性"]) {
//在此处将result赋值给currentObject的某个属性
}
}
// 5. 解析文档结束
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
NSLog(@"文档结束 %@", self.dataArray);
// 清空临时数据
self.currentObject = nil;
[self.elementString setString:@""];
}
// 6. 解析出错
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(@"解析出现错误!");
// 清空临时数据
self.currentObject = nil;
[self.elementString setString:@""];
// 清空数组
[self.dataArray removeAllObjects];
}
@end
使用GDataXML解析XML数据
//在使用GDataXML前要做得工作:
//1.需要将Libxml2.tbd添加到项目中。
//2.将/usr/include/libxml2和libxml2.dylib路径添加到项目 头文件的搜索路径中。
//3.由于GDataXML不支持ARC,所以需要手动关闭类的ARC,也就是在该类的编辑阶段添加-fno-objc-arc选项。
- (void)viewDidLoad {
[super viewDidLoad];
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://...."]];
//调用GDataXMLDocument的初始化方法
GDataXMLDocument *XMLDocument = [[GDataXMLDocument alloc] initWithData:data options:0 error:nil];
//调用GdataXMLDocument对象的rootElenent方法获取该文档的根元素
GDataXMLElement *myRootElement = [XMLDocument rootElement];
//获取myRootElement下所有<test/>元素的内容
NSArray *elementArray = [myRootElement elementsForName:@"test"];
//获取根元素之后根据XML元素之间的父子关系逐层遍历,访问该XML文档的每个元素。
for (GDataXMLElement *testElement in elementArray)
{
//通过[testElement elementsForName:@""]方法可以取出元素的内容。
}
}