Custom modal window with block completion handler

I am stuck!

I am trying to create my own modal dialogue. I would like it to execute in the same way as the NSSavePanel, using the block as a completion handler.

I copied only the important fragments that I think are necessary.

@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"); 

Everything is going well, however, after the makeKeyAndOrderFront: modalToWindow: sourceRect: completeHandler: method has completed, it does not block the stream, so "Errg" will be printed even if the user did not select "ok" or "cancel". The modal window is displayed at this moment when the user clicks “OK” and then the _handler block is executed. However, if I try to access the local variables in the block, and the application crashes because everything is already cleared.

What is the best approach to blocking the main thread from makeKeyAndOrderFront: ... method? Is this the right approach to implement a completion handler using blocks?

+4
source share
1 answer

Your line

 _handler=[handler retain]; 

it should be

 _handler=[handler copy]; 

This should solve your problem so that local variables disappear before calling the completion handler. [handler copy] takes care of the local variables mentioned in the block, so that the local variables do not disappear even after the program flow exits the method in which you made the block.

Remember the following facts:

  • A block instance captures the local variables related to the block.
  • However, the block instance is on the stack. It will disappear even if you save it when the program flow leaves the area {...} in which you create the block.
  • So, you need to copy it, not just retain , if you want to use the data afterwards, as you do here. copy automatically using retain all the local variables of the object that reference the block.
  • You need to release it as soon as you are done with it. It frees memory for the block itself and sends a release message to the specified local variables of the object. If you use GC, you do not need to worry about it.

To understand more detailed information about the block, I found the article here by Mike Ash very useful.

+5
source

Source: https://habr.com/ru/post/1299470/


All Articles