I've had need to save custom objects to file and then restore them the next time my app runs. Fortunately, this is a snap when you use the wonderful thing that is NSCoding, a protocol defined in the Foundation framework that specifies the following two required methods:
These messages are sent to an object that is about to be encoded or decoded (respectively) by an NSCoder object. As an example, say we have the following class:
@interface CustomObject : NSObject <NSCoding> { NSArray *someArray; NSInteger someInteger; BOOL someBool; } // Method definitions here @end
When archiving a CustomObject, we'd like to save the values of all of its instance variables. To do this, we can add implementations of the NSCoding methods, like so:
@implementation CustomObject // Other method implementations here - (void) encodeWithCoder:(NSCoder*)encoder { // If parent class also adopts NSCoding, include a call to // [super encodeWithCoder:encoder] as the first statement. [encoder encodeObject:someArray forKey:@"someArray"]; [encoder encodeInteger:someInteger forKey:@"someInteger"]; [encoder encodeBool:someBool forKey:@"someBool"]; } - (id) initWithCoder:(NSCoder*)decoder { if (self = [super init]) { // If parent class also adopts NSCoding, replace [super init] // with [super initWithCoder:decoder] to properly initialize. // NOTE: Decoded objects are auto-released and must be retained someArray = [[decoder decodeObjectForKey:@"someArray"] retain]; someInteger = [decoder decodeIntegerForKey:@"someInteger"]; someBool = [decoder decodeBoolForKey:@"someBool"]; } return self; } @end
That's it! Now when we attempt to archive our object, the archiver automatically sends the encodeWithCoder: message to our object, and we encode our data into the encoder. Similarly, when we unarchive our object, the archiver sends the initWithCoder: message to a newly-allocated instance of the class, and the object will come back to us exactly as it was before it was archived. Neat!
For archiving an object, do something like the following:
CustomObject *object; // Assume this exists NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:object]; // Now do something with your NSData object, such // as write it to a file or save it to NSUserDefaults
For unarchiving an object, do something like the following:
NSData *encodedObject; // First retrieve your NSData object by reading it // from a file or grabbing it from NSUserDefaults CustomObject *object = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
Yes, it really is that simple! Plus, since NSCoding is part of the Foundation framework, this exact code will work on both the Mac and iPhone platforms.
For examples of implementation, check out the CHDataStructures framework — all of the data structures conform to the NSCoding protocol. (Good examples of this include the CHAbstractBinarySearchTree and CHMutableArrayHeap classes.)
For more information on NSCoding and related classes, check out the Archives and Serializations Programming Guide.