Hi Ben,

I think this undo approach should work fine.

Another way to 'centralise' undo is to have an object listen for the KVO 
notifications of changes for properties it's interested in, and use the 
observation method to record to the undo manager. You'd still use 
setValue:forKey: at undo time, so it amounts to a very similar idea. I've done 
it that way and it works.

So your problem must be something else. Are you sure undoManager returns 
something?

Also, more obvious now I come to think about it, is that setValue:forKey: is 
not called for every property when it is set! That method can be used to call 
down to the setter for a named property, but if the setter is invoked directly 
(the more usual case), then setValue:forKey: isn't invoked. You might want to 
check whether that is what's happening. In which case making your undo handler 
a KVO observer will get around that problem, or you could override 
-willChangeValueForKey: instead, which is the method that causes some of the 
KVO "magic" to happen.

--Graham



On 08/05/2012, at 9:11 AM, Ben Kennedy wrote:

> I have a custom data model object with a number of properties of various 
> basic types.  An array of these is managed by an NSArrayController and bound 
> to an NSTableView (as the reader might recall from my earlier thread).  I am 
> not using Core Data.
> 
> I am now wiring in undo support.  Were the data backed by an 
> NSManagedObjectContext, I would get undo behaviour for free, but since there 
> isn't, I need to write my own setters to handle it.
> 
> It seems onerous, verbose and error-prone to have to implement a custom stub 
> setter method for every property like so:
> 
> - (void)setBlah:(Blah *)newBlah
>       {
>       if (newBlah != blah)
>               {
>               [undoManager registerUndoWithTarget:self 
> selector:@selector(setBlah:) object:blah];
>               [blah release];
>               blah = [newBlah copy];
>               }
>       }
> 
> Thus, I have tried to be clever by doing the following override:
> 
> - (void)setValue:(id)value forKey:(NSString *)key
>       {
>       NSArray *undoableKeys = [NSArray arrayWithObjects: @"blah", @"foo", 
> @"anotherProperty", nil];
>       
>       if ([undoableKeys containsObject:key])
>               {
>               [[undoManager prepareWithInvocationTarget:self] setValue:[self 
> valueForKey:key] forKey:key];
>               }
>       
>       [super setValue:value forKey:key];
>       
>       return;
>       }
> 
> However, the undo event does not seem to be recorded -- the Undo menu command 
> remains ghosted.  It is not clear to me why.
> 
> It wouldn't surprise me to be admonished for such an abuse of 
> setValue:forKey:, but is there a better way?  I imagine this idiom must be 
> extremely commonplace, but I could not find a clear directive.
> 
> I looked to Apple's "DemoMonkey" sample for its undo practices, and it just 
> implements the verbose boilerplate for each property.  Is that really the 
> recommended solution?

_______________________________________________

Cocoa-dev mailing list ([email protected])

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [email protected]

Reply via email to