< Previous PageNext Page > Hide TOC

Legacy Support for Non-Keyed Archives

Keyed archives are available only in Mac OS X version 10.2 and later. If your application must run in earlier versions, you cannot make use of keyed archives. There should typically be no need be no need to retrofit support for non-keyed archiving to a class originally written to support keyed archiving. Since non-keyed archiving is deprecated, you should add support for keyed archiving to classes that currently use only non-keyed archiving.

Contents:

Updating a Class to Use Keyed Coding
Supporting Keyed and Non-Keyed Archiving
Converting Coding Methods After a Class Has Been Archived to Keyed Archives
Distinguishing an NSArchiver Archive From an NSKeyedArchiver Archive


Updating a Class to Use Keyed Coding

Updating your classes to use keyed coding is not difficult. In most cases, your NSCoding methods need to assign keys only to the values being encoded and decoded. Special issues come up if you still need to handle sequential archives. Here are recommendations for changing your class to do keyed coding:

Supporting Keyed and Non-Keyed Archiving

The following example shows how you can implement archiving for a class "MapView" to support keyed and non-keyed archiving. This example assumes that the superclass of MapView also supports the NSCoding protocol. If the superclass of your class does not support NSCoding, you should omit the lines that invoke the superclass’s encodeWithCoder: and initWithCoder:methods.

- (void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];
    if ([coder allowsKeyedCoding]) {
        [coder encodeObject:mapName forKey:@"MVMapName"];
        [coder encodeFloat:magnification forKey:@"MVMagnification"];
        [coder encodeObject:legendView forKey:@"MVLegend"];
        [coder encodeConditionalObject:auxiliaryView forKey:@"MVAuxView"];
    }
    else {
        [coder encodeObject:mapName];
        [coder encodeValueOfObjCType:@encode(float) at:&magnification];
        [coder encodeObject:legendView];
        [coder encodeConditionalObject:auxiliaryView];
    }
}
 
- (id)initWithCoder:(NSCoder *)coder {
 
    self = [super initWithCoder:coder];
    if ([coder allowsKeyedCoding]) {
        // Can decode keys in any order
        mapName = [[coder decodeObjectForKey:@"MVMapName"] retain];
        legendView = [[coder decodeObjectForKey:@"MVLegend"] retain];
        auxiliaryView = [[coder decodeObjectForKey:@"MVAuxView"] retain];
        magnification = [coder decodeFloatForKey:@"MVMagnification"];
    }
    else {
        // Must decode keys in same order as encodeWithCoder:
        mapName = [[coder decodeObject] retain];
        [coder decodeValueOfObjCType:@encode(float) at:&magnification];
        legendView = [[coder decodeObject] retain];
        auxiliaryView = [coder decodeObject];
    }
    return self;
}

When unarchiving data from a sequential archive, the corresponding unarchiving code must follow exactly the same sequence of data types. Matching these is important, as the method originally used determines the format of the encoded data.

Converting Coding Methods After a Class Has Been Archived to Keyed Archives

Because NSKeyedArchiver also implements the non-keyed coding methods that it inherits from NSCoder, a class that has not been updated for keyed coding can still be encoded into a keyed archive. This can happen if the application creating the archive has been updated to use NSKeyedArchiver, but a class has not, so it still uses the old style encoding methods. The class’s instance variables are written to the archive without keys, just like for a sequential archive. If this may have happened for one of your classes, when you update your class, you must be able to handle the case where although the unarchiver supports keyed coding, the object’s instance variables were not encoded with keys. If this occurs, you must decode the values as if they are coming from a non-keyed archive. In other words, you must decode the values in the same sequence and with the matching non-keyed decoding methods as when encoded.

The simplest technique is to use a version key of some sort. When (after conversion), the object encodes itself, it needs to write a special keyed value which indicates that the object was encoded using keyed coding methods. At decoding time, if the coder allows keyed coding and this special key exists, then initWithCoder: knows that not only is this a keyed archive, but keyed coding methods were also used. If the key does not exist, initWithCoder: must still use the old decoding algorithm.

- (id)initWithCoder:(NSCoder *)coder {
    if ([coder allowsKeyedCoding]
                && [coder containsValueForKey:@"UsesKeyedCoding"] ) {
        // Use keyed coding methods
    }
    else {
        // Use non-keyed coding methods
    }
    return self;
}

Distinguishing an NSArchiver Archive From an NSKeyedArchiver Archive

Ideally, you should use a different file extension for a new document format which is keyed-archiving-based rather than non-keyed-archiving-based. If this is not possible, they you can look at the first few bytes of the archived data (the “magic number”). If the data is at least 13 bytes long, and the 2nd-13th bytes are \00btypedstream or \00bstreamtyped then you have an old archive. A suitable test is illustrated in the following code fragment:

if (13 <= dataLength &&
        ((databytes[1] == 0xb && 0 == memcmp(databytes + 2, "typedstream", 11)) ||
        (databytes[1] == 0xb && 0 == memcmp(databytes + 2, "streamtyped", 11)))) {
    // non-keyed archive ...


< Previous PageNext Page > Hide TOC


© 2002, 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-02-04)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.