obj-c编程13:归档
这篇归档内容的博文也挺有趣的,笨猫对好玩的东西一向感兴趣啊!如果用过ruby就会知道,obj-c里的归档类似于ruby中的序列化概念,不过从语法的简洁度来说,我只能又一次呵呵了。
下面大家将会看到2种归档数据的方法:属性列表和带键值的编码,前者可以理解为简单的类似字典(比如还有数组)对象的归档,而后者可以实现任意对象的归档化哦。我们依次道来吧。
1. 属性列表(plists)
os x上的程序使用类似字典类概念为支持的XML属性列表(或plists)存储默认参数等配置信息之类的数据,以前“老式”的属性列表数据格式不是XML,现在我们一般都用XML格式啊。属性列表不仅可以是NSDictionary对象,还可以是NSString、NSArray、NSData或是NSNumber类型,可以使用这些类中实现的writeToFile:atomically方法将数据写到文件中去。特别的如果是字典或数组的情况下,保存到文件的格式是XML属性列表格式哦。
#import <Foundation/Foundation.h>
#define msg(...) NSLog(__VA_ARGS__)
int main(int argc,char *argv[])
{
@autoreleasepool{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:\
@"val0",@"key0",@"val1",@"key1",@"val2",@"key2",nil];
NSDictionary *dict_from_file;
//归档
if([dict writeToFile:@"data.db" atomically:YES] == NO)
msg(@"save to file failed!");
//读档
dict_from_file = [NSDictionary dictionaryWithContentsOfFile:\
@"data.db"];
msg(@"%@",dict_from_file);
}
return 0;
}
其中atomically参数是一种保护措施,如果被设为YES,则现将字典内容写入临时备份文件中去,成功后才最终将数据写入指定文件data.db中去。我们看一下返回结果:
apple@kissAir: objc_src$./gd
2014-07-03 15:32:43.263 gd[1991:507] {
key0 = val0;
key1 = val1;
key2 = val2;
}
apple@kissAir: objc_src$cat data.db
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>key0</key>
<string>val0</string>
<key>key1</key>
<string>val1</string>
<key>key2</key>
<string>val2</string>
</dict>
</plist>
很简单,是吧?如果要在跨平台程序中使用属性列表,可以查看一下NSPropertyListSerialization类,使用该类在文件中读写属性列表可以在不同平台之间移植。
2.带键值的编码归档
接下来看一看比简单的属性列表稍微复杂,但也更加通用的带键值的编码归档方法。这就是利用NSKeyedArchiver类创建带键(keyed)的档案来完成,它是从os x 10.2开始支持的哦。在此之前是使用NSArchiver类创建连续的(sequential)归档,这有个不方便的地方:连续归档需要完全按写入时的顺序读取归档中的数据。下面我们来看看,对于简单的字典对象如何归档和读档喽:
#import <Foundation/Foundation.h>
#define msg(...) NSLog(__VA_ARGS__)
int main(int argc,char *argv[])
{
@autoreleasepool{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:\
@"val0",@"key0",@"val1",@"key1",@"val2",@"key2",nil];
NSDictionary *dict_from_file;
//归档
if([NSKeyedArchiver archiveRootObject:dict toFile:@"data.archive"] == NO)
msg(@"save to file failed!");
//读档
//dictionaryWithContentsOfFile如果失败会引发异常,这个和以前的返回NO不一样哦。
dict_from_file = [NSKeyedUnarchiver unarchiveObjectWithFile:\
@"data.archive"];
msg(@"%@",dict_from_file);
}
return 0;
}
运行结果如下:
apple@kissAir: objc_src$./gd
2014-07-03 15:47:02.411 gd[2017:507] {
key0 = val0;
key1 = val1;
key2 = val2;
}
apple@kissAir: objc_src$cat data.archive
bplist00?!"X$versionX$objectsY$archiverT$top??$null?
???????Tkey1Tkey0Tkey2Tval1Tval0Tval2?Z$classnameX$classes\NSDictionary? XNSObject_NSKeyedArchiver?#$Troot#-27AGNVahlnprvxz|~???????????????%?
注意如果查看data.archive文档会发现其不是人类可读的文档格式,而是类似于一种二进制格式哦。
上面这些归档和读档方法是很不错,但是想要直接用在我们自己的类中,没门啊!因为编译器不知道我们自己的类以何种表示方式归档呢。所以聪明的童鞋一定猜到了方法,在类中添加自己的归档和读档方法吧!
要归档自己的对象,必须遵守<NSCoding>协议,在类中添加encodeWithCoder和initWithCoder方法,前者用来归档,后者自然是读档啦。下面我们构造2个类Player和Skill类,不同的球员自然有不同的技能啦,比如司令塔,钢铁防线什么的...貌似要跑题啊!STOP!大家直接看代码吧,虽然比较长,但注释钢钢的啊:
#import <Foundation/Foundation.h>
#define msg(...) NSLog(__VA_ARGS__)
@interface Skill:NSObject <NSCoding> //maybe <NSCoding,NSCopying> :)
@property NSString *name;
@property int level;
-(id)init:(NSString*)name :(int)level;
-(NSString*)description;
-(void)use;
@end
@implementation Skill
@synthesize name,level;
-(id)init:(NSString*)name_v :(int)level_v{
self = [super init];
if(self){
name = name_v;
level = level_v;
}
return self;
}
-(NSString*)description{
return [NSString stringWithFormat:@"[skill:%@ , level:%i]",name,level];
}
-(void)use{
msg(@"%@ is used!!!",self);
}
-(void)encodeWithCoder:(NSCoder *)encoder{
[encoder encodeObject: name forKey:@"Skill#name"];
[encoder encodeInt: level forKey:@"Skill#level"];
}
-(id)initWithCoder:(NSCoder *)decoder{
name = [decoder decodeObjectForKey:@"Skill#name"];
level = [decoder decodeIntForKey:@"Skill#level"];
return self;
}
@end
@interface Player:NSObject <NSCoding>
@property NSString *name;
@property int number;
@property NSMutableArray *skills;
-(NSString*)description;
@end
@implementation Player
@synthesize name,number,skills;
-(id)init{
//需要为skills对象分配空间啊!否者skills打印的是NULL
self = [super init];
if(self){
skills = [[NSMutableArray alloc] init];
}
return self;
}
-(NSString*)description{
return [NSString stringWithFormat:@"[%@:NO.%i] skills:\n%@",\
name,number,skills];
}
-(void)encodeWithCoder:(NSCoder *)encoder{
[encoder encodeObject: name forKey:@"Player#name"];
[encoder encodeInt: number forKey:@"Player#number"];
[encoder encodeObject: skills forKey:@"Player#skills"];
}
-(id)initWithCoder:(NSCoder *)decoder{
name = [decoder decodeObjectForKey:@"Player#name"];
number = [decoder decodeIntForKey:@"Player#number"];
skills = [decoder decodeObjectForKey:@"Player#skills"];
return self;
}
@end
int main(int argc,char *argv[])
{
@autoreleasepool{
Player *p0 = [[Player alloc] init];
Skill *s0 = [[Skill alloc] init:@"super_lead" :3]; //司令塔
Skill *s1 = [[Skill alloc] init:@"steel_defend" :2]; //钢铁防守
Skill *s2 = [[Skill alloc] init:@"steal_master" :2]; //抢断大师
Skill *s3 = [[Skill alloc] init:@"spiritual_leader" :1];//精神领袖
p0.name = @"messi";
p0.number = 10;
//利用数组的addObject方法加入每个技能
[p0.skills addObject:s0];
[p0.skills addObject:s1];
[p0.skills addObject:s2];
[p0.skills addObject:s3];
msg(@"%@",p0);
//像单个对象那样归档吧
if([NSKeyedArchiver archiveRootObject:p0 toFile:@"player.arch"] == NO)
msg(@"save to archiving failed!");
//像单个对象那样读档吧
Player *p1 = [NSKeyedUnarchiver unarchiveObjectWithFile:@"player.arch"];
msg(@"%@",p1);
}
return 0;
}
例子比较长,但很简单哦,运行结果如下:
apple@kissAir: objc_src$./gd
2014-07-03 17:00:15.623 gd[2260:507] [messi:NO.10] skills:
(
"[skill:super_lead , level:3]",
"[skill:steel_defend , level:2]",
"[skill:steal_master , level:2]",
"[skill:spiritual_leader , level:1]"
)
2014-07-03 17:00:15.626 gd[2260:507] [messi:NO.10] skills:
(
"[skill:super_lead , level:3]",
"[skill:steel_defend , level:2]",
"[skill:steal_master , level:2]",
"[skill:spiritual_leader , level:1]"
)
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:\
@"val0",@"key0",@"val1",@"key1",@"val2",@"key2",nil];
NSMutableData *buf = [NSMutableData data];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] \
initForWritingWithMutableData: buf];
//将Player对象和字典对象一起归档
[arch encodeObject:p0 forKey:@"Player"];
[arch encodeObject:dict forKey:@"Dict"];
//结束在buf中添加对象,我称之为“封口”
[arch finishEncoding];
//归档写入文件
if([buf writeToFile: @"mobjs.arch" atomically:YES] == NO){
msg(@"arch to file failed!");
return 1;
}
//将Player对象和字典对象一起读档(解档)
NSData *buf_read = [NSData dataWithContentsOfFile:@"mobjs.arch"];
if(!buf_read){
msg(@"read file : mobjs.arch failed!");
return 2;
}
NSKeyedUnarchiver *unarch = [[NSKeyedUnarchiver alloc] \
initForReadingWithData: buf_read];
Player *p2 = [unarch decodeObjectForKey:@"Player"];
NSDictionary *dict1 = [unarch decodeObjectForKey:@"Dict"];
//别忘了也要完成一下哦
[unarch finishDecoding];
msg(@"dict:%@",dict1);
msg(@"player:%@",p2);
2014-07-03 17:34:23.090 gd[2362:507] dict:{
key0 = val0;
key1 = val1;
key2 = val2;
}
2014-07-03 17:34:23.091 gd[2362:507] player:[messi:NO.10] skills:
(
"[skill:super_lead , level:3]",
"[skill:steel_defend , level:2]",
"[skill:steal_master , level:2]",
"[skill:spiritual_leader , level:1]"
)
:#import <Foundation/Foundation.h>
#define msg(...) NSLog(__VA_ARGS__)
#define mstr(x) [NSMutableString stringWithString:x]
int main(int argc,char *argv[])
{
@autoreleasepool{
NSData *buf;
NSMutableArray *ary1 = [NSMutableArray arrayWithObjects:\
mstr(@"one"),mstr(@"two"),mstr(@"three"),nil];
NSMutableArray *ary2;
NSMutableString *mstr;
//使用归档进行深拷贝,没有实际读取文件,走的是内存路线啊!
buf = [NSKeyedArchiver archivedDataWithRootObject: ary1];
ary2 = [NSKeyedUnarchiver unarchiveObjectWithData: buf];
mstr = [ary2 objectAtIndex:0];
[mstr appendString:@"_fixed"];
msg(@"%@\n****************************%@",ary1,ary2);
}
return 0;
}
apple@kissAir: objc_src$./6
2014-07-03 17:45:58.972 6[2414:507] (
one,
two,
three
)
****************************(
"one_fixed",
two,
three
)
obj-c编程13:归档的更多相关文章
- 并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- msp430项目编程13
msp430中项目---温湿度检测系统 1.dht11工作原理 2.电路原理说明 3.代码(显示部分) 4.代码(功能实现) 5.项目总结 msp430项目编程 msp430入门学习
- 进阶Java编程(13)反射与Annotation
1,反射取得Annotation信息 从JDK1.5之后Java提供了Annotation技术支持,这种技术为项目的编写带来了新的模型,而后经过了十年的发展,Annotation的技术得到了非常广泛的 ...
- VMware 虚拟化编程(13) — VMware 虚拟机的备份方案设计
目录 目录 前文列表 备份思路 备份算法 备份细节 连接到 vCenter 还是 ESXi 如何选择快照类型 是否开启 CBT 如何获取备份数据 如何提高备份数据的传输率 备份厚置备磁盘和精简置备磁盘 ...
- Win64 驱动内核编程-13.回调监控模块加载
回调监控模块加载 模块加载包括用户层模块(.DLL)和内核模块(.SYS)的加载.传统方法要监控这两者加在必须 HOOK 好几个函数,比如 NtCreateSection 和 NtLoadDriver ...
- [WCF编程]13.并发:服务并发模式
一.概述 传入的客户端调用消息会分发给Windows I/O线程池(线程默认为1000)上的服务实例.多个客户端可以发起多个并发的调用,并且服务可以在多个线程上处理这些请求.如果传入的调用分发给同一个 ...
- 高级UNIX环境编程13 守护进程
linux下,keventd守护进程为内核中运行的执行的函数提供进程上下文 bdflush,kupdated将高速缓存中的数据冲洗到磁盘上
- Linux - 简明Shell编程13 - 用户输入(UserInput)
脚本地址 https://github.com/anliven/L-Shell/tree/master/Shell-Basics 示例脚本及注释 1 - arguments #!/bin/bash i ...
- Linux编程 13 (系统环境变量位置, 环境变量持久化)
一.系统环境变量位置 在上章中,知道了如何修改系统环境变量,如PATH变量,以及创建自己的全局环境变量和局部环境变量.这篇学习怎么让环境变量的作用持久化.在此之前,先了解下系统环境变量文件会在哪些位置 ...
随机推荐
- GDAL库三个读取Jpeg2000格式驱动测试
0.目的 GDAL库中提供了四五种读取Jpeg2000的驱动,但是各个驱动读取数据的效率各不相同,下面就针对三种读取jpeg2000的效率进行测试. GDAL库中提供的读取Jpeg2000的驱动有下面 ...
- Django使用forms来实现评论功能
貌似Django从版本1.6开始就放弃了对自带的comments的使用,具体原因未查,但是现在使用Django的内部的模块也可以实现评论功能,那就是借助于forms模块,下面是我的一个小例子. 环境准 ...
- Python爬虫! 单爬,批量爬,这都不是事!
昨天做了一个煎蛋网妹子图的爬虫,个人感觉效果不错.但是每次都得重复的敲辣么多的代码(相比于Java或者其他语言的爬虫实现,Python的代码量可谓是相当的少了),就封装了一下!可以实现对批量网址以及单 ...
- 2、Android构建本地单元测试
如果你的单元测试在Android中没有依赖或者只有简单的以来,你可以在你的本地开发环境中运行你的测试.这种测试比较高效因为它能让你避免将整个app安装到物理设备或虚拟机中执行单元测试.最后,执行单元测 ...
- Ext JS 6开发实例(一)
很久没写文章了,主要原因和大家差不多,都要为生活奔忙,搞了两个小项目.这两个小项目很凑巧,都可以使用Ext JS来开发,这正是练习使用Ext JS 6的好机会,自然不会错过. 很多读者可能会问,为什么 ...
- AngularJS进阶(三十八)上拉加载问题解决方法
AngularJS上拉加载问题解决方法 项目中始终存在一个问题:当在搜索栏输入关键词后(见图1),按照既定的业务逻辑应该是服务端接收到请求后,首先返回查询的前7条数据,待客户端出现上拉加载时,继续查找 ...
- Android 实现Json数据解析,并进行应用!
从网站上获取数据然后再客户端进行解析是常见的数据交互.下面是常用的一些接口网址: webservice工厂接口 http://www.36wu.com 快递查询接口http://webservice. ...
- 【翻译】Ext JS 6 Beta发布
原文:Ext JS 6 Beta is Now Available 概述 Ext JS 6的好处 新的Ext JS功能和工具 需要你的反馈意见 概述 很高兴,Ext JS 6 beta版本现在发布了. ...
- (NO.00003)iOS游戏简单的机器人投射游戏成形记(六)
为什么要将手臂移动的代码单独放在一个方法中? 其实这里是多次重构之后的版本.原来的移动代码是放在touchMoved方法里的.后来发现除了触摸手臂移动方式外,还要实现触摸屏幕移动手臂这第二种方式. 所 ...
- 【算法导论】图的深度优先搜索遍历(DFS)
关于图的存储在上一篇文章中已经讲述,在这里不在赘述.下面我们介绍图的深度优先搜索遍历(DFS). 深度优先搜索遍历实在访问了顶点vi后,访问vi的一个邻接点vj:访问vj之后,又访问vj的一个邻接点, ...