2

I'm trying to save the content of a view into a PDF file.The code you see is inside a class which inherits from NSView:

- (IBAction) savePDF: (id) sender
{
    __block NSSavePanel* panel=[NSSavePanel savePanel];
    [panel setAllowedFileTypes: [NSArray arrayWithObject: @"pdf"]];
    [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result)
     {
         if(result==NSOKButton)
         {
             NSCAssert(panel!=nil,@"panel is nil");
             NSData* data=[self dataWithPDFInsideRect: [self bounds]];
             NSError* error;
             BOOL successful=[data writeToURL: [panel URL] options: 0 error: &error];
             if(!successful)
             {
                 NSAlert* alert=[NSAlert alertWithError: error];
                 [alert runModal];
             }
         }
     }];
    panel=nil;
}

The method is triggered with a menu.
The problem is that the assertion fails:

NSCAssert(panel!=nil,@"panel is nil");

Even if I declared the NSSavePanel pointer to be __block.Why?

Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187

2 Answers2

2

Actually, the answer is to remove the __block specifier.

I ran your code with/without it. Assert is failing with it, working without it (the __blockspecifier, that is).

Now, to answer the why:

I suppose the __block specifier was made for global/instance variables, not local variables, I could be wrong though.


X'D ... I don't know what hit me, but check this:

NSSavePanel* panel=[NSSavePanel savePanel];

[panel setAllowedFileTypes: [NSArray arrayWithObject: @"pdf"]];
[panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result)
 {
     if(result==NSOKButton)
     {
         dispatch_async(dispatch_get_main_queue(), ^{
             NSCAssert(panel!=nil,@"panel is nil");
             NSData* data=[self dataWithPDFInsideRect:[self bounds]];
             NSError* error;
             BOOL successful=[data writeToURL: [panel URL] options: 0 error: &error];
             if(!successful)
             {
                 NSAlert* alert=[NSAlert alertWithError: error];
                 [alert runModal];
             }
         });
     }
 }];
panel=nil;

I just decided, let me wrap it in a GCD block, and draw it on the main thread. Your code is working splendidly. The view is being drawn to the PDF as expected, and I can confirm that :D.

As for the issue, it seems quite obvious. Calling bounds in a background thread and drawing is a no-no.

This was fun, thanks :D

Mazyod
  • 22,319
  • 10
  • 92
  • 157
  • Yes, I use ARC.I've tried to remove the __block specifier.Now the assertion doesn't fail, the file gets saved.But when I open the file with preview there's an empty PDF file.This because also [self bounds] doesn't return the correct value, if I try to print it it says that [self bounds].size.width is zero inside the block.How to let these variables survive inside the block? – Ramy Al Zuhouri Aug 05 '12 at 12:02
1

The solutions: for an unknown reason, which is still a mistery to me, if I make an IBAction to the view class, [self bounds] retuns always NSZeroRect.Inside other methods it returns the correct values.So I solved this problem removing the __block specifier from the NSSavePanel and rewriting (also re-binding) the method in the app delegate class.

Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
  • Yeah, I ran your code, and bounds is also returning CGRectZero .. I'll check it out. I am interested to know, too. – Mazyod Aug 05 '12 at 15:42