Re: Execution of Replaced Method Jumps back to top -- How??
Re: Execution of Replaced Method Jumps back to top -- How??
- Subject: Re: Execution of Replaced Method Jumps back to top -- How??
- From: Jerry Krinock <email@hidden>
- Date: Fri, 21 Aug 2009 11:13:12 -0700
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 (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
_______________________________________________
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