Archiving an
object
(or a collection of interconnected objects, known as a graph) into a
NSData
representation is often useful or
necessary. Objects that are archived into an
NSData
object can be transported over network
connections or interprocess communication channels and saved to the
filesystem. Later, the original graph of objects can be reconstituted
from the archive data.
Foundation provides five classes to support the creation and
extraction of archives, all subclasses of NSCoder
:
NSArchiver
NSUnarchiver
NSKeyedArchiver
NSKeyedUnarchiver
NSPortCoder
NSCoder
declares the common interface for
encoding and decoding objects and other Objective-C data types. For
example, the encodeObject
method encodes an object
into an archive, and methods such as encodeInt
:
and encodeRect
: support encoding C data types such
as integers and common Cocoa data structures.
NSCoder
does not implement these methods; it is an
abstract class. Rather, subclasses implement the appropriate methods
for their particular purpose.
NSArchiver
and
NSUnarchiver
provide a straightforward way of encoding
and decoding objects and scalars, but they have limitations. The
biggest limitation is that objects in an archive can be decoded only
in the same order in which they were encoded. Because of this
constraint, changing an encoding system is difficult once it has been
established publicly.
The
NSKeyedArchiver
and
NSKeyedUnarchiver
classes solve this problem by associating
keys with each object and scalar encoded in an archive. Decoders can
use these keys to access an archive’s contents in a
convenient order that isn’t constrained by a design
decision made in a previous version of the application.
Keyed
archiving is not available in versions prior to Mac OS X 10.2. If
your application supports Mac OS X 10.1, then check whether the coder
passed in initWithCoder
: or
encodeWithCoder
: supports keyed archiving. To do
this, use the NSCoder
method
allowsKeyedCoding
, which returns
YES
if keyed coding is supported, and
NO
otherwise.
The last NSCoder
subclass, NSPortCoder
, encodes and decodes object proxies in
the distributed objects system. NSConnection
uses this class, and as such, you should
never have to interact with it. Chapter 6
discusses the distributed objects system in more detail.
Consider Examples Example 2-31 and Example 2-32. Example 2-31 shows the
interface for a hypothetical Employee
class. Note
in this example that the interface declaration indicates that
Employee
conforms to the
NSCoding
protocol.
Example 2-31. Employee class with support for the NSCoding protocol
@interface Employee : NSObject < NSCoding > {
NSString *firstName;
NSString *lastName;
int employeeNumber;
}
// Methods left out
@end
Example 2-32 shows a way to implement the
NSCoding
protocol for the
Employee
class.
Example 2-32. Implementing NSCoding in the Employee class
@implementation Employee - (void)setFirstName:(NSString *)newName { [newName retain]; [firstName autorelease]; firstName = newName; } - (void)setLastName:(NSString *)newName { [newName retain]; [lastName autorelease]; lastName = newName; } - (void)encodeWithCoder:(NSCoder *)encoder { if ( [encoder allowsKeyedCoding] ) { [encoder encodeObject:firstName forKey:@"First"]; [encoder encodeObject:lastName forKey:@"Last"]; [encoder encodeInt: employeeNumber forKey:@"Number"]; } else { [encoder encodeObject:firstName]; [encoder encodeObject:lastName]; [encoder encodeValueOfObjCType:@encode(int) at:&employeeNumber]; } } - (id)initWithCoder:(NSCoder *)decoder { if ( [decoder allowsKeyedCoding] ) {// These may be decoded in any order you like
employeeNumber = [decoder decodeIntForKey:@"Number"];// Returned values are autoreleased
[self setFirstName: [decoder decodeObjectForKey:@"First"]]; [self setLastName: [decoder decodeObjectForKey:@"Last"]]; } else {// These must be decoded in the same order that they
// were encoded
[self setFirstName: [decoder decodeObject]]; [self setLastName: [decoder decodeObject]]; [decoder decodeValueOfObjCType:@encode(int) at:&employeeNumber]; } return self; } @end