I’m stuck!
I am trying to create a custom modal dialog. I would like it to perform similarly to the NSSavePanel using a block as a completion handler.
I’ve copied only the important snippets I think are needed.
@implementation ModalWindowController
- (void)makeKeyAndOrderFront:(id)sender
modalToWindow:(NSWindow*)window
sourceRect:(NSRect)rect
completionHandler:(void (^)(NSInteger result))handler {
_handler = [handler retain];
session = [NSApp beginModalSessionForWindow:[self window]];
[[NSApplication sharedApplication] runModalSession:session];
[[self window] makeKeyAndOrderFrontCentered:self expandingFromFrame:rect];
}
- (IBAction)okButtonPressed:(id)sender {
[[self window] orderOut:self];
_handler(NSOKButton);
[NSApp endModalSession:session];
}
@end
Now I can call this using the code:
[self.modalWindowController makeKeyAndOrderFront:self
modalToWindow:[[self view] window]
sourceRect:sr
completionHandler:^(NSInteger result) {
NSLog(@"Inside Block");
if ( result == NSOKButton ) {
// do something interesting here
}
}];
NSLog(@"Errg");
All goes well however, after the method makeKeyAndOrderFront:modalToWindow:sourceRect:completionHandler: has completed it does not block the thread, so “Errg” will be printed even though the user has not selected “ok” or “cancel”. The modal window is displayed at this point, where the user clicks OK and the _handler block is then executed. However if I am trying to access local variables in the block, and the app crashes as everything has cleaned up already.
What is the best approach to blocking the main thread from the makeKeyAndOrderFront:… method? Is this the right approach to implementing a completion handler using blocks?
Your line
should be
That should solve your problem, that the local variables are gone before the completion handler is called.
[handler copy]takes care of the local variables referred to in the block, so that the local variables don’t go away even after the flow of the program exited the method where you made the block.Remember the following facts:
{...}in which you create the block.copyit, not justretainit, if you want to use the data afterwards, as you are doing here.Copying it automaticallyretains all the local object variables referred to from the block.releaseit once you’re done with it. It deallocates the memory for the block itself, and sendsreleasemessage to the local object variables referred to. If you use GC, you don’t have to care about this, though.To understand more details of the block, I found the article here by Mike Ash very helpful.