Re: Experience with keyed archiving forward/backwards compatibility?
Re: Experience with keyed archiving forward/backwards compatibility?
- Subject: Re: Experience with keyed archiving forward/backwards compatibility?
- From: Uli Kusterer <email@hidden>
- Date: Wed, 28 Aug 2013 20:38:50 +0200
On Aug 28, 2013, at 4:53 PM, Marcel Weiher <email@hidden> wrote:
> does anyone have practical experience with the forward/backward compatibility aspect of keyed archiving? That is define a file format using keyed archiving where backward and forward compatibility was both desirable and achieved?
Hi,
I've written code that was supposed to be future-safe with NSKeyedArchiver, and I've written raw binary file access code that was future-safe, but the archiver code I wrote hasn't actually been tested in practice AFAIK (i.e. I never got to see incompatible changes being made). So while I can give you the practice of writing the code, I can't actually confirm that it's bug-free.
FWIW, it's usually fairly simple: You can add new fields with new keys without any extra effort, the old version will just ignore them. The new version's -initWithCoder: just has to be written to cope with these fields not being there, and in that case create a default object/assign a default value. encodeWithCoder: doesn't have to know about this, since you've initialized all the ivars with defaults, it can just write the newest version.
If that for some reason isn't an option, you can simply write out a version number for each object in encodeWithCoder (just an int with 1 is fine for starters). In initWithCoder:, if the int isn't there or is 0, you assume it's the old format, if it is 1, you know it's the new format and read the new keys. If you want to be really future-safe, you also define that e.g. the bottom 16 bits of the int are the minor revision, and the top 16 bits the newer one. If the minor version changes, that means you can read the file, you just won't get all the data (i.e. you increment the minor version when a change is a compatible change) while if the major version changes, you have a new file the old version of the app can't read.
If you did your own binary writing code, you'd probably only need one version for the entire file. However, keyed archiving is encapsulated, per-object. You can have objects from various frameworks, each of which can change independently. This is a good, clean design, but means you need to save the version again for each class. There's no way around that either, as you don't really get a guarantee which object gets de-serialized first, so unless you write the version number to the file manually, then write the serialized object NSData after it, you have no way of reading the version number separately from the other objects to decide whether you can read the file.
But you have a clean, compartmentalized OO design that automatically copes with individual objects being modified.
Note that every class has a version (+setVersion: and +version) that you can use to store the current version number. You still have to write it out, but the neat thing is your comparison code can initially just compare and write out that current version for every class, and only when it actually changes, you hard-code a particular number different from 0.
You can't really use keyed archivers if you want to be able to open and save the new file format with an old version. For one, you can't preserve the data that the old version doesn't know what to do with. You can't anticipate what to keep around (e.g. if you add an index, edit the file in the old version, but carry over the index unchanged, it'll be out of date, leading to wrong search results at best, crashes at worst). In this case I'd recommend you actually build a plist representation, and decide on some convention or separation that makes it easy to determine what can be kept around, and what needs to be deleted because the old version can't keep it in sync. Either a way of naming the keys, or a compatible/incompatible version number scheme like above, maybe again per object. Also, if you add a new class, the old application won't be able to de-serialize it from a keyed archiver. However, keyed archivers work if you only need to be able to open files created by the old version in a new version of the app.
Cheers,
-- Uli Kusterer
"The Witnesses of TeachText are everywhere..."
http://www.zathras.de
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden