views:

97

answers:

1

I'm using an OpenPanel to get a file path URL. This works:

[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
 NSURL *pathToFile = nil;

 if (returnCode == NSOKButton)
     pathToFile = [[oPanel URLs] objectAtIndex:0];
}];

This doesn't, resulting in an 'assignment of read-only variable' error:

NSURL *pathToFile = nil;
[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
 if (returnCode == NSOKButton)
     pathToFile = [[oPanel URLs] objectAtIndex:0];
}];
return pathToFile;

In general, any attempt to extract pathToFile from the context of oPanel has failed. This isn't such a big deal for small situations, but as my code grows, I'm forced to stuff everything -- XML parsing, core data, etc -- inside an inappropriate region. What can I do to extract pathToFile?

Thanks.

+2  A: 

This doesn't, resulting in an 'assignment of read-only variable' error:

NSURL *pathToFile = nil;
[oPanel beginSheetModalForWindow:theWindow completionHandler:^(NSInteger returnCode)
{
 if (returnCode == NSOKButton)
     pathToFile = [[oPanel URLs] objectAtIndex:0];
}];
return pathToFile;

Yes, because you're trying to assign to the copy of the pathToFile variable that gets made when the block is created. You're not assigning to the original pathToFile variable that you declared outside the block.

You could use the __block keyword to let the block assign to this variable, but I don't think this will help because beginSheetModalForWindow:completionHandler: doesn't block. (The documentation doesn't mention this, but there's no reason for the method to block, and you can verify with logging that it doesn't.) The message returns immediately, while the panel is still running.

So, you're trying to have your completion-handler block assign to a local variable, but your method in which you declared the local variable will probably have returned by the time block runs, so it won't be able to work with the value that the block left will leave in the variable.

Whatever you do with pathToFile should be either in the block itself, or in a method (taking an NSURL * argument) that the block can call.

Peter Hosey
Thanks. Interesting that it doesn't follow the laws of scope.
spamguy
Scope alone cannot work because, as I said, the block will run long after the method or function it's created in has finished running. Thus, that outer scope is no longer valid when the block runs. You can use `__block` when the function or method calls the block itself, but when it passes the block off to be queued and run later, that won't work, because the function or method will have finished already by the time the block goes to assign to the variable.
Peter Hosey
This is beyond frustrating. I'm essentially barred from doing anything else until I'm done with my URL, willingly or not. So, tasks like fetching a URL and then clicking a button to use it are impossible. Why, Apple, why?
spamguy
You are not. Think asynchronously. Handle the URL in the block, or in a method that you call from the block. Don't stand around waiting for the URL to arrive; that's what the run loop is for. All you have to do, after running the panel, is return.
Peter Hosey