
How to Save & Load Custom Object ?
Usually, we use NSKeyedArchiver to serialize an object and write it to a file.
Correspondingly, NSKeyedUnarchiver is used to get the object from the file.
The NSKeyedArchiver’s interface is like this:
+ (BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path;
NSKeyedArchiver is a NSCoder. The rootObject should conform to the protocol NSCoding.
@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
@end
Lets Take One Example :
For example, now we have a customObject:
@interface CustomObject : NSObject <NSCoding> {
NSString* mStringValue;
int mIntValue;
BOOL mBOOLValue;
}
@property (non-atomic, retain) NSString *stringValue;
@property (nonatomic, assign) int intValue;
@property (nonatomic, assign) BOOL boolValue;
@endIt conforms to NSCoding, so we need to implement those two methods in the .m file
#define kEncodeKeyStringValue @"kEncodeKeyStringValue"
#define kEncodeKeyIntValue @"kEncodeKeyIntValue"
#define kEncodeKeyBOOLValue @"kEncodeKeyBOOLValue"
#pragma mark - NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.stringValue forKey:kEncodeKeyStringValue];
[aCoder encodeInt:self.intValue forKey:kEncodeKeyIntValue];
[aCoder encodeBool:self.boolValue forKey:kEncodeKeyBOOLValue];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super init]))
{
self.stringValue = [aDecoder decodeObjectForKey:kEncodeKeyStringValue];
self.intValue = [aDecoder decodeIntForKey:kEncodeKeyIntValue];
self.boolValue = [aDecoder decodeBoolForKey:kEncodeKeyBOOLValue];
}
return self;
}
Now, we can save and load a customObject like:
- (IBAction)saveObjectPlain:(id)sender {
printf("==========================================\n");
printf("saveObjectPlain===========================\n");
printf("==========================================\n");
//< Create and Save the Object
{
CustomObject *obj = [[[CustomObject alloc] init] autorelease];
obj.stringValue = @"The String Value";
obj.intValue = 12345;
obj.boolValue = YES;
[NSKeyedArchiver archiveRootObject:obj toFile:[self tempFilePath]];
printf("Save: \n %s \n", [[obj description] cStringUsingEncoding:NSUTF8StringEncoding]);
}
//< Load and Print the Object
{
CustomObject *obj = [NSKeyedUnarchiver unarchiveObjectWithFile:[self tempFilePath]];
printf("Load: \n %s \n", [[obj description] cStringUsingEncoding:NSUTF8StringEncoding]);
}
printf("==========================================\n");
}Quite easy, right?
How about to save/load an array of our CustomObjects?
Since NSArray conforms to protocol NSCoding, it is quite straightforward.
- (IBAction)saveObjectsInArray:(id)sender {
printf("==========================================\n");
printf("saveObjectsInArray========================\n");
printf("==========================================\n");
//< Create Two Keys and Save the Object
{
CustomObject *obj1 = [[[CustomObject alloc] init] autorelease];
obj1.stringValue = @"The String Value 1";
obj1.intValue = 12345;
obj1.boolValue = YES;
CustomObject *obj2 = [[[CustomObject alloc] init] autorelease];
obj2.stringValue = @"The String Value 2";
obj2.intValue = 54321;
obj2.boolValue = NO;
NSArray *array = [NSArray arrayWithObjects:obj1, obj2, nil];
[NSKeyedArchiver archiveRootObject:array toFile:[self tempFilePath]];
printf("Save: \n %s \n ", [[array description] cStringUsingEncoding:NSUTF8StringEncoding]);
}
//< Load and Print the Object
{
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:[self tempFilePath]];
printf("Load: \n %s \n ", [[array description] cStringUsingEncoding:NSUTF8StringEncoding]);
}
printf("==========================================\n");
}So, if there is a member variable of type NSArray in our CustomObject,
we need to make sure the array contains only objects conform to NSCoding.
Otherwise, an exception of unrecognized selector [.. encodeWithCoder:] will be raised.
One step further, how to save/load a NSDictionary has a CustomObject as the key and another CustomObject as the Object.
This is actually another problem. We need to make sure CustomObject can be used as a key in the NSDictionary.
According to the documentation, NSDictionary requires its key to conform to the protocol NSCopying.
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
The key object needs to be copied to a specified piece of memory described by NSZone.
The implementation is like:
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone {
CustomObject *copy = [[CustomObject allocWithZone:zone] init];
copy.stringValue = [[self.stringValue copy] autorelease];
copy.intValue = self.intValue;
copy.boolValue = self.boolValue;
return copy;
}
We use allocWithZone to allocate a CustomObject on the indicated piece of memory.
Then copy member variables to the new instance.
The save/load procedure is like:
- (IBAction)saveObjectAsKey:(id)sender {
printf("==========================================\n");
printf("saveObjectAsKey===========================\n");
printf("==========================================\n");
//< Create Two Keys and Save the Object
{
CustomObject *keyObj = [[[CustomObject alloc] init] autorelease];
keyObj.stringValue = @"The String Value Key";
keyObj.intValue = 12345;
keyObj.boolValue = YES;
CustomObject *valObj = [[[CustomObject alloc] init] autorelease];
valObj.stringValue = @"The String Value Value";
valObj.intValue = 54321;
valObj.boolValue = NO;
NSDictionary *dict = [NSDictionary dictionaryWithObject:valObj forKey:keyObj];
[NSKeyedArchiver archiveRootObject:dict toFile:[self tempFilePath]];
printf("Save: \n %s \n", [[dict description] cStringUsingEncoding:NSUTF8StringEncoding]);
}
//< Load and Print the Object
{
NSDictionary *dict = [NSKeyedUnarchiver unarchiveObjectWithFile:[self tempFilePath]];
printf("Load: \n %s \n", [[dict description] cStringUsingEncoding:NSUTF8StringEncoding]);
}
printf("==========================================\n");
}Hope it helps! :)
Reference Links :
1. http://haoxiang.org/2011/11/ios-how-to-save-and-load-a-custom-object/