19.4 使用NSData创建自定义档案

有时可能不想和前面示例程序一样,使用archiveRootObject:ToFile:方法将对象直接写入文件。比如,可能想收集一些或全部对象,并将其存储到单个档案文件中。在Objective-C中,通过使用名为NSData的通用数据流对象类,可以实现上述功能,在第16章,我们简单地提到过这个类。

正如第16章所提到的,NSData对象可以用来保留一块内存空间以备后来存储数据。这些数据空间的典型应用是作为一些数据的临时存储空间,如随后将被写入文件,或可能用于容纳从磁盘读取的文件内容。创建可变数据空间的最简单方式是使用data方法:


dataArea=[NSMutableData data];


该语句创建一个空缓冲区,其大小随程序执行需要而扩展。

作为一个简单的例子,假设你想将地址簿和一个Foo对象归档到同一个文件。假设对于这个例子,你已经向AddressBook和AddressCard类添加了一个带键的归档方法(参见代码清单19-9)。

代码清单19-9


import<Foundation/NSObject.h>

import<Foundation/NSAutoreleasePool.h>

import<Foundation/NSString.h>

import<Foundation/NSKeyedArchiver.h>

import<Foundation/NSCoder.h>

import<Foundation/NSData.h>

import“AddressBook.h”

import“Foo.h”

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

Foo*myFoo1=[[Foo alloc]init];

Foo*myFoo2;

NSMutableData*dataArea;

NSKeyedArchiver*archiver;

AddressBook*myBook;

//Insert code from Program 19.7 to create an Address Book

//in myBook containing four address cards

[myFoo1 setStrVal:@“This is the string”];

[myFoo1 setIntVal:12345];

[myFoo1 setFloatVal:98.6];

//Set up a data area and connect it to an NSKeyedArchiver object

dataArea=[NSMutableData data];

archiver=[[NSKeyedArchiver alloc]

initForWritingWithMutableData:dataArea];

//Now we can begin to archive objects

[archiver encodeObject:myBook forKey:@“myaddrbook”];

[archiver encodeObject:myFoo1 forKey:@“myfoo1”];

[archiver finishEncoding];

//Write the archived data area to a file

if([dataArea writeToFile:@“myArchive”atomically:YES]==NO

NSLog(@“Archiving failed!”);

[archiver release];

[myFoo1 release];

[pool drain];

return 0;

}


分配一个NSKeyedArchiver对象之后,发送initForWritingWithMutableData:消息,以指定要写入归档数据的存储空间。这就是前面创建的NSMutabledata空间dataArea。此时,就可以向存储在archiver中的NSKeyedArchiver对象发送编码消息,以归档该程序中的对象。实际上,所有编码消息在收到finishEncoding消息之前都被归档并存储在指定的数据空间内。

这里,有两个对象需要编码—一个是地址簿,另一个是Foo对象。对于这些对象可以使用encodeObject:forKey:,因为在前面你已经为AddressBook、AddressCard和Foo类实现了编码方法和解码方法(理解这个概念很重要)。

在归档这两个对象时,向archiver对象发送一条finishEncoding消息。之后,就不能编码其他对象,你需要发送此消息以完成归档过程。

此时,你预留的那块名为dataArea的空间包含归档对象,这些对象可以一种可写入文件的格式存在。消息表达式


[dataArea writeToFile:@“myArchive”atomically:YES]


向你的数据流发送writeToFile:atomically:encoding:error:消息,请求它把它的数据写入指定文件,这个文件名为myArchive。

从if语句可以看到,writeToFile:atomically:encoding:error:方法返回一个BOOL值:如果写操作成功就返回YES,如果失败(可能是指定了无效的路径名或文件系统已满)就返回NO。

从档案文件中恢复数据很简单—所做的工作只需和归档文件相反。首先,需要像以前那样分配一个数据空间。然后,把档案文件中的数据读入该数据空间,然后,需要创建一个NSKeyedUnarchiver对象,并告知它从指定空间解码数据。必须调用解码方法来提取和解码归档的对象,做完之后,向NSKeyedUnarchiver对象发送一条finishDecoding消息。

代码清单19-10实现了所有任务。

代码清单19-10


import<Foundation/NSObject.h>

import<Foundation/NSAutoreleasePool.h>

import<Foundation/NSString.h>

import<Foundation/NSKeyedArchiver.h>

import<Foundation/NSCoder.h>

import<Foundation/NSData.h>

import“AddressBook.h”

import“Foo.h”

int main(int argc, char*argv[])

{

NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];

NSData*dataArea;

NSKeyedUnarchiver*unarchiver;

Foo*myFoo1;

AddressBook*myBook;

//Read in the archive and connect an

//NSKeyedUnarchiver object to it

dataArea=[NSData dataWithContentsOfFile:@“myArchive”];

if(!dataArea){

NSLog(@“Cant read back archive file!”);

return(1);

}

unarchiver=[[NSKeyedUnarchiver alloc]

initForReadingWithData:dataArea];

//Decode the objects we previously stored in the archive

myBook=[unarchiver decodeObjectForKey:@“myaddrbook”];

myFoo1=[unarchiver decodeObjectForKey:@“myfoo1”];

[unarchiver finishDecoding];

[unarchiver release];

//Verify that the restore was successful

[myBook list];

NSLog(“@\n%i\n%g”,[myFoo1 strVal],

[myFoo1 intVal],[myFoo1 floatVal]);

[pool release];

return 0;

}


代码清单19-10输出


========Contents of:Steves Address Book=========

Jamie Baker jbaker@hitmail.com

Julia Kochan

jewls337@axlc.com

Stephen Kochan steve@steve_kochan.com

Tony Iannino tony.iannino@techfitness.com

===================================================

This is the string

12345

98.6


输出结果验证了你的地址簿和Foo对象已成功地从档案文件中恢复了。