归档
概念:在Objective-c术语中,归档是指用某种格式来保存一个对象,以便以后还原这些对象的过程。通常这个过程包括将(多个)对象写入文件中,以便以后读回该对象。
两种归档数据的方法:属性列表和带键值的编码。
一、使用XML属性列表进行归档
1、XML属性列表:MAC OS X上的应用程序使用XML属性列表(或plists)束来存储诸如默认参数选择,应用程序设置和配置信息这样的数据。
注:
1>、:这些列表的归档用途是有限的,因为当为某个数据结构创建属性列表时,没有保存特定的对象类,没有存储对同一对象的多个引用,也没有保持对象的可变性。
2>、属性列表归档只针对于Foundation对象
2、将数据以XML格式写出:当你的对象是NSString、NSDictionary、NSArray、NSData或NSNumber对象,你可以使用在这些类中实现的writeToFile: atomically: 方法将数据写到文件中。
3、将XML属性列表读入程序:使用dictionaryWithContentsOfFile: 或arrayWithContentsOfFile:方法。
注:读回数据使用dataWithContentsOfFile: 方法,要读回字符串对象,使用stringWithContentsOfFile; 方法。
4、例--
#import <Foundation/NSObject.h> #import <Foundation/NSString.h> #import <Foundation/NSDictionary.h> #import <Foundation/NSAutoreleasePool.h> int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSDictionary *glossary =[NSDictionary dictionaryWithObjectsAndKeys: @"A class defined so other classes can inherit from it.", @"abstract class", @"To implement all the methods defined in a protocol", @"adopt", @"Storing an object for later use. ", @"archiving", nil ]; if ([glossary writeToFile: @"glossary" atomically: YES] == NO) NSLog (@"Save to file failed! "); [pool drain]; return 0; }
希望首先将字典写入临时备份文件中,写入成功后,将把最终数据转移到名为glossary的指定文件中。这项安全措施可使得文件在执行操作的过程中崩溃时免受破坏。
glossary文件的内容如下:
<?xml version= "1.0" encoding= "UTF-8 "?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version= "1.0"> <dict> <key>abstract class</key> //属性列表 <string>A class defined so other classes can inherit from it.</string> <key>adopt</key> <string>To implement all the methods defined in a protocol</string> <key>archiving</key> <string>Storing an object for later use. </string> </dict> </plist>
注意:根据字典创建属性列表时,字典中的键必须全是NSString对象。字典中的值可以是NSString、NSArray、 NSDictionary、 NSData、 NSNumber对象。
#import <Foundation/NSObject.h> #import <Foundation/NSString.h> #import <Foundation/NSDictionary.h> #import <Foundation/NSEnumerator.h> #import <Foundation/NSAutoreleasePool.h> int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSDictionary *glossary; glossary = [NSDictionary dictionaryWithContentsOfFile: @"glossary"]; for ( NSString *key in glossary ) NSLog (@"%@: %@", key, [glossary objectForKey: key]); [pool drain]; return 0; }
注意:属性列表文件不必一定由程序创建,可以来自文本编辑器,或/Developer/Applications/Utilities下的Property List Editor。
二、使用NSKeyedArchiver归档
1、NSArchiver类:创建连续的(sequential)归档。连续的归档需要完全按照写入时的顺序读取归档中的数据。
2、NSKeyedArctiver类:创建带键的归档:归档某个对象时,会为它提供一个名称,即键。
注:从归档中检索该对象时,是根据这个键来检索它的。这样,可以按照任意的顺序将对象写入归档并进行检索。
注意:iPhone SDK中没有提供NSArchiver。如果想在iPhone上使用归档功能,则必须使用NSKeyedArchiver。
3、使用要求:需要导入<Foundation/NSKeyedArchiver.h>。
4、写入文件:archiveRootObject:toFile:方法将对象存储到磁盘上。
#import <Foundation/NSObject.h> #import <Foundation/NSString.h> #import <Foundation/NSDictionary.h> #import <Foundation/NSKeyedArchiver.h> #import <Foundation/NSAutoreleasePool.h> int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSDictionary *glossary =[NSDictionary dictionaryWithObjectsAndKeys: @"A class defined so other classes can inherit from it", @"abstract class", @"To implement all the methods defined in a protocol", @"adopt", @"Storing an object for later use", @"archiving", nil ]; [NSKeyedArchiver archiveRootObject: glossary toFile: @"glossary.archive"]; [pool release]; return 0; }
5、从文件读取:通过NSKeyedUnarchiver的unArchiverObjectWithFile:方法将创建的归档文件读入执行程序中。
#import <Foundation/NSObject.h> #import <Foundation/NSString.h> #import <Foundation/NSDictionary.h> #import <Foundation/NSEnumerator.h> #import <Foundation/NSKeyedArchiver.h> #import <Foundation/NSAutoreleasePool.h> int main (int argc, char *argv[]){ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSDictionary *glossary; glossary = [NSKeyedUnarchiver unarchiveObjectWithFile:@"glossary.archive"]; for ( NSString *key in glossary ) NSLog (@"%@: %@", key, [glossary objectForKey: key]); [pool drain]; return 0; }
三、编码方法和解码方法
1、通常自定义类对象不能直接归档,必须告知系统如何归档(或编码)你的对象,以及如何解归档(解码)它们。
2、要求:必须遵守<NSCoding>协议,在类定义中添加encodeWithCoder: 方法和initWithCoder: 方法以实现归档对象和恢复对象。
注:编码方法一般应指定如何归档想要保存的对象中的每个实例变量。
3、在带键的档案中编码和解码基本数据类型
编码方法
解码方法
encodeBool: forKey
decodeBool: forKey
encodeInt: forKey
decodeInt: forKey
encodeInt31: forKey
decodeInt32: forKey
encodeInt64: forKey
decodeInt64: forKey
encodeFloat: forKey
decodeFloat: forKey
encodeDouble: forKey
decodeDouble: forKey
注:对于基本的Objective-C类,可以使用encodeObject:forKey:和decodeObjectforKey:编码和解码。
4、例--
======== Addresscard.h接口文件========
#import <Foundation/Object.h> #import<Foundation/NSString.h> #import<Foundation/NSKeyedArchiver.h> @interface AddressCard : NSObject <NSCopying, NSCoding> { NSString *name; NSString *email; } @property (nonatomic, copy) NSString *name, *email; -(void) setName: (NSString *) theName andEmail: (NSString *) theEmail; -(NSComparisonResult) compareNames: (id) element; -(void) print //Additional methods for NSCopying protocol -(AddressCard *) copyWithZone: (NSZone *) zone; -(void) retainName: (NSString *) theName andEmail: (NSString *) theEmail; @end
======== 添加到AddressCard类实现文件方法========
-(void) encodeWithCoder: (NSCoder *) encoder { [encoder encodeObject: name forKey: @”AddressCardName”]; [encoder encodeObject: email forKey: @”AddressCardEmail”]; } -(id) initWithCoder: (NSCoder *)decoder { Name=[[decoder decodeObjectforKey: @”AddressCardName”]retain]; Emame=[[decoder decodeObjectforKey: @”AddressCardEmail”]retain]; }
四、使用NSData创建自定义档案
1、有时可能不希望使用archiveRootObject:ToFile:方法将对象直接写入文件,而是临时放到有NSData对象创建的临时存储空间中保存。
2、NSData类:可以收集一些或全部对象,并将其存储到单个档案文件中。
3、NSData对象可以用来保留一块内存空间以备后来存储数据。这些数据空间的典型应用是作为一些数据的临时存储空间,如随后将被写入文件,或可能用于容纳从磁盘读取的文件内容。
4、例---
========编码=========
#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 to a file if ( [dataArea writeToFile: @"myArchive" atomically: YES] == NO) NSLog (@"Archiving failed!"); [archiver release]; [myFoo1 release]; [pool drain]; return 0; }
========解码=======
#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 (@"Can’t 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; }
五、使用归档程序复制对象
1、简介:使用Foundation的归档能力可以创建对象的深复制。
2、例--
#import <Foundation/NSObject.h> #import <Foundation/NSAutoreleasePool.h> #import <Foundation/NSString.h> #import <Foundation/NSKeyedArchiver.h> #import <Foundation/NSArray.h> int main (int argc, char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSData *data; NSMutableArray *dataArray = [NSMutableArray arrayWithObjects: [NSMutableString stringWithString: @"one"], [NSMutableString stringWithString: @"two"], [NSMutableString stringWithString: @"three"], nil ]; NSMutableArray *dataArray2; NSMutableString *mStr; // Make a deep copy using the archiver data = [NSKeyedArchiver archivedDataWithRootObject: dataArray]; dataArray2 = [NSKeyedUnarchiver unarchiveObjectWithData: data]; mStr = [dataArray2 objectAtIndex: 0]; [mStr appendString: @"ONE"]; NSLog (@"dataArray: "); for ( NSString *elem in dataArray ) NSLog ("%@", elem); NSLog (@"\ndataArray2: "); for ( NSString *elem in dataArray2 ) NSLog ("%@", elem); [pool drain]; return 0; }