这篇归档内容的博文也挺有趣的,笨猫对好玩的东西一向感兴趣啊!如果用过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]"

)

    我们还可以把任意多个对象归档到文件中去,这就用到了内存归档技术啦,即使用NSData的通用数据流对象类,可以实现上述功能哦!注意这个类似乎很熟悉吧?对啊,在第10[3]和10[4]文件管理篇里有提到啊!
    NSData可以用来保留一块内存空间来存储数据,以便随后写入文件或存放从文件读取的内容。以下语句创建一个空缓冲区,其大小将随着程序执行需要而扩展:
buf = [NSMutableData data];
下面将演示如何在一个归档和读档多个对象(只列出在上个代码基础上新添加的部分,否则太长):
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:归档的更多相关文章

  1. 并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  2. msp430项目编程13

    msp430中项目---温湿度检测系统 1.dht11工作原理 2.电路原理说明 3.代码(显示部分) 4.代码(功能实现) 5.项目总结 msp430项目编程 msp430入门学习

  3. 进阶Java编程(13)反射与Annotation

    1,反射取得Annotation信息 从JDK1.5之后Java提供了Annotation技术支持,这种技术为项目的编写带来了新的模型,而后经过了十年的发展,Annotation的技术得到了非常广泛的 ...

  4. VMware 虚拟化编程(13) — VMware 虚拟机的备份方案设计

    目录 目录 前文列表 备份思路 备份算法 备份细节 连接到 vCenter 还是 ESXi 如何选择快照类型 是否开启 CBT 如何获取备份数据 如何提高备份数据的传输率 备份厚置备磁盘和精简置备磁盘 ...

  5. Win64 驱动内核编程-13.回调监控模块加载

    回调监控模块加载 模块加载包括用户层模块(.DLL)和内核模块(.SYS)的加载.传统方法要监控这两者加在必须 HOOK 好几个函数,比如 NtCreateSection 和 NtLoadDriver ...

  6. [WCF编程]13.并发:服务并发模式

    一.概述 传入的客户端调用消息会分发给Windows I/O线程池(线程默认为1000)上的服务实例.多个客户端可以发起多个并发的调用,并且服务可以在多个线程上处理这些请求.如果传入的调用分发给同一个 ...

  7. 高级UNIX环境编程13 守护进程

    linux下,keventd守护进程为内核中运行的执行的函数提供进程上下文 bdflush,kupdated将高速缓存中的数据冲洗到磁盘上

  8. Linux - 简明Shell编程13 - 用户输入(UserInput)

    脚本地址 https://github.com/anliven/L-Shell/tree/master/Shell-Basics 示例脚本及注释 1 - arguments #!/bin/bash i ...

  9. Linux编程 13 (系统环境变量位置, 环境变量持久化)

    一.系统环境变量位置 在上章中,知道了如何修改系统环境变量,如PATH变量,以及创建自己的全局环境变量和局部环境变量.这篇学习怎么让环境变量的作用持久化.在此之前,先了解下系统环境变量文件会在哪些位置 ...

随机推荐

  1. 【java集合框架源码剖析系列】java源码剖析之LinkedList

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本. 在实际项目中LinkedList也是使用频率非常高的一种集合,本博客将从源码角度带领大家学习关于LinkedList的知识. ...

  2. Portlet开发入门实例

    1原生Portlet开发 这是最简单.最本质的开发方式,直接基于Portlet规范定义的接口开发Portlet.优点是贴近底层比较灵活, 缺点当然就是所有事情都要自己去做.就好比不用SpringMVC ...

  3. How to migrate data from another Mac using Mountain Lion and earlier

    链接:http://support.apple.com/zh-cn/HT4889

  4. jquery实战---标签页效果

    在前面的博客中,小编主要简单的介绍了jquery的一些基本知识,今天这篇博文,小编继续来学习jquery的相关知识,今天我们来学习一个标签页的小例子,相关源码小编已经上传,有需要的小伙伴可以自己去下载 ...

  5. UNIX网络编程——套接字选项(SO_RCVBUF和SO_SNDBUF)

    有时候我们需要控制套接字的行为(如修改缓冲区的大小),这个时候我们就要学习套接字选项. int getsockopt(int sockfd,int level,int optname,void *op ...

  6. Java-IO之FileDescriptor

    FileDescriptor是文件描述符,可以被用来表示开放文件,开放套接字等,FileDescriptor可以被看成某个文件,但无法对该文件进行操作,需要新创建FileDescriptor对应的Fi ...

  7. 【一天一道LeetCode】#93. Restore IP Addresses

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...

  8. flexbox基本原理

    新版的flexbox规范分两部分:一部分是container,一部分是 items. flexbox是一整套布局规范,包含了多个css属性,所以学习起来比`float: left;` 这样简单的布局要 ...

  9. 调用awk的三种方式

    调用awk的三种方式 调用awk有三种方式,一种为Shell命令行方式,另外两种是将awk程序写入脚本文件,然后执行该脚本文件.三种方式的命令格式归纳如下: 一.在Shell命令行输入命令调用awk, ...

  10. UNIX环境高级编程——标准I/O库函数和Unbuffered I/O函数

    以写文件为例,C标准I/O库函数(printf(3) .putchar(3) .fputs(3) )与系统调用write(2) 的关 系如下图所示. 库函数与系统调用的层次关系 open .read ...