Object Serialization using NSCoder and NSCoding

One of the neat features of Cocoa is how easy it makes it to serialize objects to various standard formats for storing in a file, or transmission over The Internet.

We’ve covered how to serialize objects to Property Lists (AKA ‘Plists’) and JSON previously, but by default only a small set of standard object types can be saved in this way (e.g. NSDictionary, NSArray, NSString, etc.)

What if you want to save a custom object type; one that you have created yourself? In theory you can write methods to convert your own objects to and from dictionaries or strings, but this is cumbersome, and there are lots of nasty edge cases to think about, such as circular references. There is a better way:

Cocoa provides an abstract class called NSCoder, along with a protocol called NSCoding. Together, these are designed to standardise the process of converting classes to and from serialized data formats. You’ve probably come across NSCoding in some capacity if you’ve ever used nib files or storyboards, because NSCoding is the underlying mechanism that Cocoa uses for loading views and view controllers from nibs.

Many Cocoa classes (including all the standard collections, like NSArray, NSSet and NSDictionary) already support NSCoding, but implementing the NSCoding protocol in your own classes is simple – here is how you would do it for a hypothetical class called “Foo”, which has three properties:

@interface Foo : NSObject 
 
@property (nonatomic, assign) NSInteger property1;
@property (nonatomic, assign) BOOL property2;
@property (nonatomic, copy) NSString *property3;
 
@end
 
@implementation Foo
 
- (id)initWithCoder:(NSCoder *)coder
{
    if ((self = [super init]))
    {
        // Decode the property values by key, and assign them to the correct ivars
        _property1 = [coder decodeIntegerForKey:@"property1"];
        _property2 = [coder decodeBoolForKey:@"property2"];
        _property3 = [coder decodeObjectForKey:@"property3"];
    }
    return self;
}
 
- (void)encodeWithCoder:(NSCoder *)coder
{
    // Encode our ivars using string keys
    [coder encodeInteger:_property1 forKey:@"property1"];
    [coder encodeBool:_property2 forKey:@"property2"];
    [coder encodeObject:_property3 forKey:@"property3"];
}
 
@end

That’s all very well, but how do those methods get called, and where does the NSCoder object come from? Implementing a concrete subclass of NSCoder is non-trivial, but fortunately Cocoa already provides a standard implementation in the form of the NSKeyedArchiver/Unarchiver.

NSKeyedArchiver is a subclass of NSCoder that knows how to save objects as a binary Plist. The Plists generated by NSKeyedArchiver can be opened and viewed in an ordinary Property List editor, but the internal structure is complicated, and not designed to be hand-edited.

To save an instance of the Foo object to a file, you’d just write:

Foo *someFoo = [[Foo alloc] init];
[NSKeyedArchiver archiveRootObject:someFoo toFile:someFile];

And to load it again later, you’d write:

Foo *someFoo = [NSKeyedUnarchiver unarchiveObjectWithFile:someFile];

This is nice, simple, high-performance way to save data files without all the boilerplate and complexity of other more sophisticated data persistence methods such as Core Data or SQLite.

NSCoding also takes care of all those nasty problems like recursion and circular references inside your objects, so whether you’re saving a single object or a complex object hierarchy, It Just Works™.


Nick Lockwood is the author of iOS Core Animation: Advanced Techniques. Nick also wrote iCarousel, iRate and other Mac and iOS open source projects.
  1. I sometimes use this approach along with the DAO (data access object) design pattern.

    This means defining a protocol for your data access methods such as:

    – (NSArray) listCustomersByFirstName:(NSString*)firstName
    – (void)saveCustomer:(Customer*)customer

    . . this way, if you decide that more sophisticated persistence is required later, you can plug in another implementation of the interface.

    The Typhoon Framework example shows this: https://github.com/typhoon-framework/Typhoon-example

Comments are closed.