| [ Team LiB ] |
|
Using NSData to Create Custom ArchivesYou might not want to write your object directly to a file using the archiveRootObject:ToFile: method, as was done in the previous program examples. For example, perhaps you want to collect some or all of your objects and store them in a single archive file. This can be done in Objective-C using the general data stream object class called NSData, which we briefly visited in Chapter 16. As mentioned in Chapter 16, an NSData object can be used to reserve an area of memory into which you can store data. Typical uses of this data area might be as temporary storage for data that will subsequently be written to a file or perhaps to hold the contents of a file read from the disk. The simplest way to create a mutable data area is with the data method: dataArea = [NSMutableData data]; This creates an empty buffer space whose size expands as needed as the program executes. As a simple example, let's assume you want to archive your address book and one of your Foo objects in the same file. Assume for this example that you've added keyed archiving methods to the AddressBook and AddressCard classes (see Program 19.10). If you haven't, or keyed archives aren't supported on your system, you can modify this example to work without keyed archives. Program 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];
Foo *myFoo1 = [[Foo alloc] init];
Foo *myFoo2;
NSMutableData *dataArea;
NSKeyedArchiver *archiver;
AddressBook *myBook;
// Insert code from Program 19.4 to create an Address Book
// in myBook containing four address cards
[myFoo1 setAll: @"This is the string" iVal: 12345 fVal: 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 are to a file
if ( [dataArea writeToFile: @"myArchive" atomically: YES] == NO)
printf ("Archiving failed!\n");
[archiver release];
[myFoo1 release];
[pool release];
return 0;
}
After allocating an NSKeyedArchiver object, the initForWritingWithMutableData: message is sent to specify the area in which to write the archived data; this is the NSMutabledata area dataArea you previously created. The NSKeyedArchiver object stored in archiver can now be sent encoding messages to archive objects in your program. In fact, all encoding messages up until it receives a finishEncoding message are archived and stored in the specified data area. You have two objects to encode here—the first is your address book and the second is your Foo object. You can use encodeObject: for these objects because you have previously implemented encoder and decode methods for the AddressBook, AddressCard, and Foo classes. It's important that you understand that concept. When you are done archiving your two objects, you send the archiver object the finishEncoding message. No more objects can be encoded after that point, and you need to send this message to complete the archiving process. The area you set aside and named dataArea now contains your archived objects in a form you can write to a file. The message expression [data writeToFile: @"myArchive" atomically: YES] sends the writeToFile:atomically: message to your data stream to ask it to write its data to the specified file, which you named myArchive. As you can see from the if statement, the writeToFile:atomically: method returns a BOOL value: YES if the write operation succeeds and NO if it fails (perhaps an invalid pathname for the file was specified or the file system is full). Restoring the data from your archive file is simple—you just do things in reverse. First, you need to allocate a data area like before. Next, you need to read your archive file into the data area, and then you have to create an NSKeyedUnarchiver object and tell it to decode data from the specified area. You must invoke decode methods to extract and decode your archived objects. When you're all done, you send a finishDecoding message to the NSKeyedUnarchiver object. This is all done in Program 19.11 that follows. Program 19.11
#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"];
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];
printf ("%s\n%i\n%g\n", [[myFoo1 strVal] cString],
[myFoo1 intVal], [myFoo1 floatVal]);
[pool release];
return 0;
}
Program 19.11 Output======== Contents of: Steve's Address Book ========= Jamie Baker jbaker@kochan-wood.com Julia Kochan jewls337@axlc.com Stephen Kochan steve@kochan-wood.com Tony Iannino tony.iannino@techfitness.com ==================================================== This is the string 12345 98.6 The output verifies that the address book and your Foo object were successfully restored from the archive file. |
| [ Team LiB ] |
|