4

I'm decoding a custom object from a cached serialization. I've versioned++ the object since it was encoded, and if the serialized version is an old version I want to just throw it away. I was under the impression that I could just return nil from my initWithCoder: method and all would be well. That's throwing an error though.

EDIT - Per NSObject's +version: documentation "The version number applies to NSArchiver/NSUnarchiver, but not to NSKeyedArchiver/NSKeyedUnarchiver. A keyed archiver does not encode class version numbers.". I'm using a keyed archiver so that approach won't help.

I am using my own embedded version number, not the NSObject version + NSCoder's versionForClassName:. Is that the way to go about this? Will NSCoder automatically abort attempting to decode if the Class versions don't match, or do I need to do this check manually.

I'm currently doing this:

- (id)initWithCoder:(NSCoder *)coder {
    if (![coder containsValueForKey:@"Class.User.version"]) {
        // Version information missing
        self = nil;
        return nil;
    }

    NSInteger modelVersion = [coder decodeIntForKey:@"Class.User.version"];
    if (modelVersion < USER_MODEL_VERSION) {
        // Old user model, discard
        self = nil;
        return nil;
    }

    ...
}

and I'm getting this error when it tries to decode old versions

-[__NSPlaceholderDictionary initWithObjects:forKeys:]: number of objects (0) not equal to number of keys (46)'

This all seems odd because initializers can fail for any number of reasons, and the pattern is to return nil in that case, so should this really crash a decoder?

EDIT - Per Apple's documentation it sounds like I'm approaching this the right way. There does not appear to be, however, a mechanism for completely aborting decoding if an object is old and out of date. I don't have or want an upgrade path from the old object, so what do I do?

DougW
  • 28,776
  • 18
  • 79
  • 107

1 Answers1

1

instead of setting self to nil, set all of the individual properties that you decode to nil, then return self. So, write this:

if (modelVersion < USER_MODEL_VERSION) {
    // Old user model, discard
    object1 = nil;
    object2 = nil;
    ...
    object46 = nil;
}
return self;

This will essentially return an object of the right type and all, but all properties will be nil.

Greg
  • 9,068
  • 6
  • 49
  • 91
  • Yeah, that's something that could be done but it ends up requiring me to do a two-pass decode. Once to decode and again to remove any objects with nil or a special flag set. The bigger issue I see is that it's perfectly legitimate for an init method to return nil, and they may do so for a variety of reasons aside from this one. The fact that returning nil from an init crashes a decoder seems like a bug to me. Anyway, sounds like it probably is a bug, so I'll accept this as a functional workaround in the meantime. – DougW Jul 13 '11 at 00:49
  • You are interpreting Apple's documentation as "Don't decode old, obsolete values". What it actually means is "Don't _encode_ old, obsolete values, then you don't have to decode them". So decode them once, then don't write them at the end. – Greg Jul 13 '11 at 00:54