Re: NSKeyedArchiver - better ways to autosave documents?
Re: NSKeyedArchiver - better ways to autosave documents?
- Subject: Re: NSKeyedArchiver - better ways to autosave documents?
- From: Guy English <email@hidden>
- Date: Thu, 1 Sep 2005 20:10:06 -0400
Hi Keith,
You bring up a good point about the renaming and rearranging of
nodes. Basically what you want to do is track all the user actions
that change that project part of your model. You can think of it a
lot like an undo manager - when you replay the journal you're at the
very bottom of the undo stack and you redo each action until you've
emptied the stack. So, yes, you play back in the order the actions
occurred. Each node having a unique id is great - it's going to make
things easier. Here's what I suggest and note that I haven't
implemented this for a Cocoa so it's more of a conceptual level
suggestion so the Real World may get in the way a little.
Create a class as the Journal Manager. When you create a node
you will register it with the Journal Manager. The Journal Manager
will add a 'create' action which will store the initial state of your
node. I suggest capturing the node state using the archive method
you've already got. It's good to do it this way because if you replay
an old journal that just says 'create' and you've changed the
defaults for an object since the journal was created you'll run into
discrepancy problems. This may happen if a user is working, your app
dies but has autosaved, the user downloads the new non-crashing
version of your app and loads up the project he was working on. The
new non-crashing version has different defaults for the node object
and things go south again. Unhappy user. Best to stash the state you
know the journal is working from. When a node is deleted you
deregister it from the Journal Manager which stores a 'delete' action
for the given node id.
Great, but what about the state changes in-between creation and
deletion? Here's where the magic happens. When you register a node
with the Journal Manager it uses Key Value Observing to observe the
node. You will be called each time a KVO setter is used on the node.
Simply append to the journal a 'set' action for the node id with the
key and new value. You're done. You can now track a nodes state from
creation to deletion.
Replaying follows this code-ish looking thing:
unsigned i, max;
max = [actions count];
for ( i = 0; i < max; i++ )
{
NSDictionary *action = [actions objectAtIndex: i];
switch ( [[action objectForKey: @"ActionType"] intValue] )
{
case MyActionCreate:
[self createNewNodeFromNodeState: [action objectForKey:
@"InitalNodeState"]];
break;
case MyActionDelete:
MyNode *node = [self nodeWithUniqueID: [action
objectForKey: @"uuid"]];
[self removeNode: node];
break;
case MyActionSetValue:
MyNode *node = [self nodeWithUniqueID: [action
objectForKey: @"uuid"]];
[node setValue: [action objectForKey: @"value"] forKey:
@"key"];
break;
default:
// panic!
break;
}
}
Now ... here's where Real Life gets to be an ass about things.
First ... you're using KVC compliant methods, right? If you're not
you'll need to do things the hard way. Second ... you need to know
what keys on the node to observe. There are a few routes to this. You
can use NSObjects +exposeBinding in the node class to advertise the
bindings and -exposedBindings in the Journal Manager to work out
which keys to observe. Or you can make your own method which the
Journal Manager will call that will return an array of keys to
observe. Both of these methods require a bit of maintenance - each
time you add a property you need to expose it to the Journal Manager.
Certainly it's less maintenance than doing things without KVO and
it's not very error prone ( just append a string to an array ) but
it's code you need to remember to write. Alternatively you can try to
guess all the KVO keys a class supports by poking around the runtime.
I've got a category on NSObject that'll do that but ... well, I'm not
sure it's such a good idea. You'll end up logging changes to keys you
may not want to.
Anyway, from there I think you've got a good shot at getting a
decent implementation. Tricky points include that the *parent* node
will get the 'set' action when a child is repositioned in it's list
and remembering that by using Key Value Coding in the journal file
you expose object APIs to the file format. Remove an accessor or ivar
and be prepared to deal with it in the Journal Manager. Before
applying a set action make sure the key is one you allow the journal
to be setting for instance.
Hope that helps,
Guy
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden