1 使用归档的方式保存文件
1.1 问题
归档是任何对象都可以实现的更常规的方式,可以进行归档的对象需要实现NSCoding协议,而且每个实例变量应该是基本数据类型或者是实现NSCoding协议的某个类的实例。本案例使用归档NSKeyedArchiver和反归档NSKeyedUnarchiver将TRPerson对象写入和读取文件。
1.2 方案
首先创建一个TRPerson类,继承至NSObject,该类有两个属性NSString类型的name和NSInteger类型的age。
其次TRPerson遵守NSCoding协议,实现协议方法initWithCoder:和encodeWithCoder:,initWithCoder:方法主要实现解码,encodeWithCoder:方法主要实现编码。
然后在main函数中创建三个TRPerson对象,并且放入persons数组中保存,persons数组就是需要进行归档的对象。
首先实现数据归档,先需要创建一个NSMutableData对象mdata,用于存放归档之后的数据。然后创建NSKeyedArchiver对象archiver,使用initForWritingWithMutableData:方法进行初始化,data参数传递的就是刚才创建的mdata对象。
再使用encodeObject:forKey:方法进行编码,编码需要指定一个key,反归档时也需要用的这个key,两个可以要保持一致,这里将key设置为“person”。最后使用finishEncoding结束编码。
使用writeToFile:atomically:方法将mdata写入到本地文件保存。
然后实现数据的反归档,先使用dataWithContentsOfFile:获取一个data对象,然后创建NSKeyedUnarchiver对象unarchiver,使用initForReadingWithData:进行初始化,data参数就是刚才获取的data对象。
再使用decodeObjectForKey:方法进行解码获取到一个数组对象persons2,解码时的key要与编码时使用的key保持一致,所以这里的key参数传递“person”。最后使用finishDecoding结束解码。
最后将persons2数组输出,在控制台查看结果。
1.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建TRPerson类
首先创建一个TRPerson类,继承至NSObject,该类有两个属性NSString类型的name和NSInteger类型的age,代码如下所示:
- @interface TRPerson : NSObject <NSCoding>
- @property (nonatomic, copy) NSString * name;
- @property (nonatomic) NSInteger age;
- @end
其次TRPerson遵守NSCoding协议,实现协议方法initWithCoder:和encodeWithCoder:,initWithCoder:方法主要实现解码,encodeWithCoder:方法主要实现编码,代码如下所示:
- - (id)initWithCoder:(NSCoder *)aDecoder
- {
- self = [super init];
- if (self) {
- //解码
- self.name = [aDecoder decodeObjectForKey:@"name"];
- self.age = [aDecoder decodeIntegerForKey:@"age"];
- }
- return self;
- }
- - (void)encodeWithCoder:(NSCoder *)aCoder
- {
- //编码
- [aCoder encodeObject:self.name forKey:@"name"];
- [aCoder encodeInteger:self.age forKey:@"age"];
- }
然后重写description方法,方便TRPerson信息的输出,代码如下所示:
- -(NSString *)description {
- return [NSString stringWithFormat:@"name:%@,age:%d",self.name,self.age];
- }
最后在main函数中创建三个TRPerson对象,并且放入persons数组中保存,persons数组就是需要进行归档的对象,代码如下所示:
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- TRPerson *person1 = [[TRPerson alloc]init];
- person1.name = @"zhangsan";
- person1.age = 20;
- TRPerson *person2 = [[TRPerson alloc]init];
- person2.name = @"lisi";
- person2.age = 18;
- TRPerson *person3 = [[TRPerson alloc]init];
- person3.name = @"wangwu";
- person3.age = 22;
- NSArray *persons = @[person1,person2,person3];
- }
- return 0;
- }
步骤二:数据归档
首先需要创建一个NSMutableData对象mdata,用于存放归档之后的数据。然后创建NSKeyedArchiver对象archiver,使用initForWritingWithMutableData:方法进行初始化,data参数传递的就是刚才创建的mdata对象,代码如下所示:
- //创建可变的data对象,需要写入数据
- NSMutableData *mdata = [NSMutableData data];
- //创建归档对象archiver
- NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:mdata];
然后使用encodeObject:forKey:方法进行编码,编码需要指定一个key,反归档时也需要用的这个key,两个可以要保持一致,这里将key设置为“person”,代码如下所示:
- //编码
- [archiver encodeObject:persons forKey:@"person"];
完成编码之后使用finishEncoding结束归档,代码如下所示:
- //结束编码
- [archiver finishEncoding];
最后使用writeToFile:atomically:方法将mdata写入到本地文件保存,代码如下所示:
- [mdata writeToFile:@"/Users/Tarena/Desktop/person" atomically:YES];
运行程序桌面上增加一个名为person的文件,存储的数据就是刚才归档的persons数组,如图-1所示:
![](http://pdf7.tarena.com.cn/tts8_source/ttsPage/IOS/ISD_V01/MODEL-STORAGE/DAY03/CASE/01/index.files/image001.png)
图-1
步骤二:数据反归档
首先使用dataWithContentsOfFile:获取一个data对象,然后创建NSKeyedUnarchiver对象unarchiver,使用initForReadingWithData:进行初始化,data参数就是刚才获取的data对象,代码如下所示:
- //获取文件数据
- NSData *data = [NSData dataWithContentsOfFile:@"/Users/Tarena/Desktop/person"];
- //创建反归档对象unarchiver
- NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];
然后使用decodeObjectForKey:方法进行解码获取到一个数组对象persons2,解码时的key要与编码时使用的key保持一致,所以这里的key参数传递“person”,完成解码之后使用finishDecoding结束反归档,代码如下所示:
- //解码
- NSArray *persons2 = [unarchiver decodeObjectForKey:@"person"];
- //结束解码
- [unarchiver finishDecoding];
- NSLog(@"%@",persons2);
最后运行程序将persons2数组输出,在控制台查看结果和persons保存的数据一样,如图-2所示:
![](http://pdf7.tarena.com.cn/tts8_source/ttsPage/IOS/ISD_V01/MODEL-STORAGE/DAY03/CASE/01/index.files/image002.png)
图-2
1.4 完整代码
本案例中,main.m文件中的完整代码如下所示:
- #import <Foundation/Foundation.h>
- #import "TRPerson.h"
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- TRPerson *person1 = [[TRPerson alloc]init];
- person1.name = @"zhangsan";
- person1.age = 20;
- TRPerson *person2 = [[TRPerson alloc]init];
- person2.name = @"lisi";
- person2.age = 18;
- TRPerson *person3 = [[TRPerson alloc]init];
- person3.name = @"wangwu";
- person3.age = 22;
- NSArray *persons = @[person1,person2,person3];
- //创建可变的data对象,需要写入数据
- NSMutableData *mdata = [NSMutableData data];
- //创建归档对象archiver
- NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:mdata];
- //编码
- [archiver encodeObject:persons forKey:@"person"];
- //结束编码
- [archiver finishEncoding];
- [mdata writeToFile:@"/Users/Tarena/Desktop/person" atomically:YES];
- //获取文件数据
- NSData *data = [NSData dataWithContentsOfFile:@"/Users/Tarena/Desktop/person"];
- //创建反归档对象unarchiver
- NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];
- //解码
- NSArray *persons2 = [unarchiver decodeObjectForKey:@"person"];
- //结束解码
- [unarchiver finishDecoding];
- NSLog(@"%@",persons2);
- }
- return 0;
- }
2 使用NSXML框架解析XML文件
2.1 问题
XML可扩展标记语言,是一种运用于各种计算机语言中的非常重要的数据交换格式,属性列表(plist)就是以XML形式存储的,本案例演示如何使用XML框架解析XML文件。
2.2 方案
首先准备一个XML文件,该XML文件保存的是图书的信息,包括书名、作者、价钱以及页数,如图-3所示:
![](http://pdf7.tarena.com.cn/tts8_source/ttsPage/IOS/ISD_V01/MODEL-STORAGE/DAY03/CASE/01/index.files/image003.png)
图-3
其次创建一个Book类用于保存解析出来的图书信息,该类继承至NSObject,有如下属性:
NSString类型的name,用于保存书名;
NSString类型的author,用于保存图书作者;
int类型的page,用于保存图书的页数;
int类型的price,用于保存图书的价钱。
然后创建一个MyParser类继承至NSObject,该类需要遵守NSXMLParserDelegate协议,主要通过实现协议方法完成XML文件的解析。
MyParser类有如下属性和方法:
NSMutableArray类型的books,用于保存解析出来的全部Book对象;
Book类型的currentBook,用于保存当前正在解析的Book对象;
NSString的currentString,用于保存当前正在解析的内容;
-(NSMutableArray*)getBooksByData:(NSData *)data方法的功能主要是创建NSXMLParser对象parser,进行XML文件解析获取到全部的Book对象,该方法中需要将parser的委托对象delegate指定为self。
最后在MyParser类中分别实现相关的协议方法,完成XML文件的解析。在main函数中就可以直接通过MyParser的getBooksByData:方法获取到所有的图书对象。
2.3 步骤
实现此案例需要按照如下步骤进行。
步骤一:创建Book对象
首先创建一个Book类用于保存解析出来的图书信息,该类继承至NSObject,代码如下所示:
- @interface Book : NSObject
- @property (nonatomic, retain) NSString *name;
- @property (nonatomic, retain) NSString *author;
- @property (nonatomic, assign) int page;
- @property (nonatomic, assign) int price;
- @end
为了方便输出Book信息,在Book类中重写description方法,代码如下所示:
- -(NSString *)description {
- return [NSString stringWithFormat:@"name:%@,author:%@,price:%d,page:%d",self.name,self.author,self.price,self.page];
- }
步骤二:MyParser类
首先创建一个MyParser类继承至NSObject,该类需要遵守NSXMLParserDelegate协议,主要通过实现协议方法完成XML文件的解析,代码如下所示:
- @interface MyParser : NSObject<NSXMLParserDelegate>
- @property (nonatomic, retain)NSMutableArray *books;
- @property (nonatomic, retain)Book *currentBook;
- @property (nonatomic, retain)NSString *currentString;
- -(NSMutableArray*)getBooksByData:(NSData *)data;
- @end
其次实现getBooksByData:方法,该方法的功能主要是创建NSXMLParser对象parser,进行XML文件解析获取到全部的Book对象,该方法中需要将parser的委托对象delegate指定为self,代码如下所示:
- -(NSMutableArray *)getBooksByData:(NSData *)data{
- self.books = [NSMutableArray array];
- NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
- parser.delegate = self;
- //XML解析
- [parser parse];
- return self.books;
- }
然后在MyParser类中分别实现相关的协议方法,完成XML文件的解析,首先需要实现parser:didStartElement:namespaceURI:qualifiedName:attributes:方法,该方法在遇到开始标签时被调用,因此需要在该方法中创建一个Book对象,用于保存当前正在解析的的图书信息,代码如下所示:
- //遇到一个开始标签时触发
- - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
- if ([elementName isEqualToString:@"book"]) {
- self.currentBook = [[Book alloc]init];
- }
- }
其次实现parser:foundCharacters:方法,该方法在遇到字符串时被调用,因此需要在该方法中保存当前获取到的信息,该信息即是图书的某个属性内容,代码如下所示:
- //遇到字符串时触发
- -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
- //获取当前信息
- self.currentString = string;
- }
再实现parser:didEndElement:namespaceURI:qualifiedName:方法,该方法在遇到结束标签时被调用,因此该方法中需要根据结束标签的内容确定self.currentString记录的信息是当前图书的具体哪一个属性的内容,代码如下所示:
- //遇到结束标签时触发
- -(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
- //根据结束标签将self.currentString赋值给相应的图书信息
- if ([elementName isEqualToString:@"name"]) {
- self.currentBook.name = self.currentString;
- }else if ([elementName isEqualToString:@"author"]) {
- self.currentBook.author = self.currentString;
- }else if ([elementName isEqualToString:@"price"]) {
- self.currentBook.price = [self.currentString intValue];
- }else if ([elementName isEqualToString:@"page"]) {
- self.currentBook.page = [self.currentString intValue];
- }else if ([elementName isEqualToString:@"book"]){
- [self.books addObject:_currentBook];
- }
- }
实现以上方法就完成了XML文件的解析,最后在main函数中直接通过MyParser的getBooksByData:方法获取到所有的图书对象,代码如下所示:
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- MyParser *parser = [[MyParser alloc]init];
- NSMutableArray *books = [parser getBooksByData:[NSData dataWithContentsOfFile:@"/Users/Tarena/Desktop/books.xml"]];
- for (Book *book in books) {
- NSLog(@"%@",book);
- }
- }
- return 0;
- }
运行程序,在控制台输出所有图书的信息,如图-4所示:
![](http://pdf7.tarena.com.cn/tts8_source/ttsPage/IOS/ISD_V01/MODEL-STORAGE/DAY03/CASE/01/index.files/image004.png)
图-4
2.4 完整代码
本案例中,main.m文件中的完整代码如下所示:
- #import <Foundation/Foundation.h>
- #import "MyParser.h"
- #import "Book.h"
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- MyParser *parser = [[MyParser alloc]init];
- NSMutableArray *books = [parser getBooksByData:[NSData dataWithContentsOfFile:@"/Users/Tarena/Desktop/books.xml"]];
- for (Book *book in books) {
- NSLog(@"%@",book);
- }
- }
- return 0;
- }
本案例中,MyParser.h文件中的完整代码如下所示:
- #import <Foundation/Foundation.h>
- #import "Book.h"
- @interface MyParser : NSObject<NSXMLParserDelegate>
- @property (nonatomic, retain)NSMutableArray *books;
- @property (nonatomic, retain)Book *currentBook;
- @property (nonatomic, retain)NSString *currentString;
- -(NSMutableArray*)getBooksByData:(NSData *)data;
- @end
本案例中,MyParser.m文件中的完整代码如下所示:
- #import "MyParser.h"
- #import "Book.h"
- @implementation MyParser
- -(NSMutableArray *)getBooksByData:(NSData *)data{
- self.books = [NSMutableArray array];
- NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
- parser.delegate = self;
- //XML解析
- [parser parse];
- return self.books;
- }
- //遇到一个开始标签时触发
- - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
- if ([elementName isEqualToString:@"book"]) {
- self.currentBook = [[Book alloc]init];
- }
- }
- //遇到字符串时触发
- -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
- //获取当前信息
- self.currentString = string;
- }
- //遇到结束标签时触发
- -(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
- //根据结束标签将self.currentString赋值给相应的图书信息
- if ([elementName isEqualToString:@"name"]) {
- self.currentBook.name = self.currentString;
- }else if ([elementName isEqualToString:@"author"]) {
- self.currentBook.author = self.currentString;
- }else if ([elementName isEqualToString:@"price"]) {
- self.currentBook.price = [self.currentString intValue];
- }else if ([elementName isEqualToString:@"page"]) {
- self.currentBook.page = [self.currentString intValue];
- }else if ([elementName isEqualToString:@"book"]){
- [self.books addObject:_currentBook];
- }
- }
- @end
本案例中,Book.h文件中的完整代码如下所示:
本案例中,Book.m文件中的完整代码如下所示:
- @implementation Book
- -(NSString *)description {
- return [NSString stringWithFormat:@"name:%@,author:%@,price:%d,page:%d",self.name,self.author,self.price,self.page];
- }
- @end