In case anyone is scratching their head over this, I have a possible answer, although I'm still deep in Undo Doodoo. The jumping back to the top is apparently caused by Cocoa's automatic "group by event" mechanism, as you can see from #2 in this call stack:

#0 0x0035603a in -[NSUndoManager(DelayEndUndoGrouping) replacement_beginUndoGrouping] at NSUndoManager+SSYAdds.m:58 #1 0x9564ba37 in -[NSUndoManager(NSUndoManagerPrivate) _prepareEventGrouping]
#2      0x9564bc12 in -[NSUndoManager beginUndoGrouping]
#3 0x00356097 in -[NSUndoManager(DelayEndUndoGrouping) replacement_beginUndoGrouping] at NSUndoManager+SSYAdds.m:64 #4 0x0034eaa5 in -[SSYLinkedOperation(Operation_Common) beginUndoGrouping_unsafe] at Operation_Common.m:28
#5      0x955e69ac in __NSThreadPerformPerform
#6      0x975243c5 in CFRunLoopRunSpecific
#7      0x97524aa8 in CFRunLoopRunInMode
#8      0x95fa32ac in RunCurrentEventLoopInMode
#9      0x95fa30c5 in ReceiveNextEventCommon
#10     0x95fa2f39 in BlockUntilNextEventMatchingListInMode
#11     0x917ad6d5 in _DPSNextEvent
#12 0x917acf88 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]
#13     0x917a5f9f in -[NSApplication run]
#14     0x917731d8 in NSApplicationMain
#15     0x00001d83 in main at MainApp-Main.m:19

And indeed, if I -setGroupsByEvent:NO, it never jumps back to the top.

But as you can also see by comparing #0 and #3 in the call stack, this is a recursive invocation. It looks like maybe the original - beginUndoGrouping method runs the private _prepareEventGrouping method which checks to see if a new event has begun which needs an undo group begun and, if so, re-invokes -beginUndoGrouping, (which invokes the replacement, in my case). Maybe it does this check in order to "clean up" any automatic "by event" grouping which needs to be done before beginning the manually-requested undo group.

Well, as Graham Cox said last week, "Where you can, avoid opening and closing undo groups yourself. A default group is opened by the event loop so if that is adequate it can save a lot of headaches."

But since the out-of-box behavior definitely doesn't work properly, I'm going to have to slog through the headaches.

On 2009 Aug 20, at 08:54, Jerry Krinock wrote:

[Re-sending this message with new details, and also because my thread was hijacked yesterday.]

In debugging my undo grouping issue, I've replaced NSUndoManager's - beginUndoGrouping and -endUndoGrouping with methods that log whenever they're invoked.

- (void)replacement_beginUndoGrouping {
  NSLog(@"WILL beginUndoGrouping for %@", self) ;    // line 1
  NSInteger oldLevel = [self groupingLevel] ;
  NSInteger localSeqNum = gSeqNum ;
  [self replacement_beginUndoGrouping] ;             // line 4
  NSLog(@" DID beginUndoGrouping level: %d->%d locSeqNum=%d",
        oldLevel, [self groupingLevel], localSeqNum) ;
  gSeqNum++ ;
}

(For those unfamiliar with Method Replacement [1], the above is NOT an infinite loop because [self replacement_beginUndoGrouping] invokes the ORIGINAL (Cocoa's) -beginUndoGrouping.)

Here's a snippet from Xcode's log while performing commands that invoke -beginUndoGrouping:

11:27:57.531 Test[804:10b] WILL beginUndoGrouping for <NSUndoManager: 0x170441d0> 11:27:57.547 Test[804:10b] WILL beginUndoGrouping for <NSUndoManager: 0x170441d0> 11:27:57.555 Test[804:10b] DID beginUndoGrouping level: 0->1 locSeqNum=5 11:27:57.556 Test[804:10b] DID beginUndoGrouping level: 0->2 locSeqNum=5

So, apparently this method is being invoked twice, but the second one starts before the first one is complete! At first I thought that this was just Xcode's console doing some of the lazy logging like Leopard's Console.app which drives me nuts. But the values of locSeqNum=5 and oldLevel=0 and the milliseconds say that these statements really ^are^ executing out of sequence. Single-stepping in the Debugger shows the same thing -- execution jumps from "line 4" back to "line 1". And note that this is all in the main thread, 10b and, yes, the same instance, 0x170441d0.

This only happens about 20% the time. Other times, statements execute as expected. One obvious possibility is that Method Replacement misbehaves and does not invoke the replacement in this repeatable test case. How can this happen?

Jerry

[1] http://developer.apple.com/SampleCode/MethodReplacement/index.html

Here's the entire implementation:

#import <objc/runtime.h>

static NSInteger gSeqNum = 0 ;

@implementation NSUndoManager (Debug20090818)

+ (void)load {
  NSLog(@"%s is replacing methods", __PRETTY_FUNCTION__) ;
  Method originalMethod ;
  Method replacedMethod ;

originalMethod = class_getInstanceMethod(self, @selector(beginUndoGrouping)) ; replacedMethod = class_getInstanceMethod(self, @selector(replacement_beginUndoGrouping)) ;
  method_exchangeImplementations(originalMethod, replacedMethod) ;

originalMethod = class_getInstanceMethod(self, @selector(endUndoGrouping)) ; replacedMethod = class_getInstanceMethod(self, @selector(replacement_endUndoGrouping)) ;
  method_exchangeImplementations(originalMethod, replacedMethod) ;
}


- (void)replacement_beginUndoGrouping {
  NSLog(@"WILL beginUndoGrouping for %@", self) ;
  NSInteger oldLevel = [self groupingLevel] ;
  [self replacement_beginUndoGrouping] ;
NSLog(@" DID beginUndoGrouping level: %d->%d", oldLevel, [self groupingLevel]) ;
}

- (void)replacement_endUndoGrouping {
  NSLog(@"WILL --endUndoGrouping for %@", self) ;
  NSInteger oldLevel = [self groupingLevel] ;
  [self replacement_endUndoGrouping] ;
NSLog(@" DID --endUndoGrouping level: %d->%d", oldLevel, [self groupingLevel]) ;
}

@end
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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:
http://lists.apple.com/mailman/options/cocoa-dev/jerry%40ieee.org

This email sent to je...@ieee.org

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

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

This email sent to arch...@mail-archive.com

Reply via email to