Is NSDocument's Saving Message Flow Threaded?

  • Hi Guys,

    I've overridden -
    canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: to
    display a custom NSAlert if any of the text files that my NSDocument
    subclass manages are not saved.

    The problem is, if the user tries to close the window and very quickly
    presses return to select the default response of "Save All Files" the
    app crashes when [self close] is called.  If they wait a second or two
    then no crash happens.  The "saveAllFiles" method writes the text
    files to the disk and then saves the NSDocument model, which holds all
    the file references.

    From my logs I can see that the [self close] causes the document to
    be dealloc'd before -dataOfType:error: can be called.  With NSZombie
    enabled I get the following log output

    2008-02-16 16:57:25.803 Scribbler[697:10b] *** -[ESDocument
    _saveDocumentWithDelegate:didSaveSelector:contextInfo:]: message sent
    to deallocated instance 0xea625c0

    Where the instance 0xea625c0 is the ESDocument that is being saved,
    then closed.

    Could this be a threading issue with the way NSDocument saves itself,
    even though I haven't put any threaded code in my app at all? I ask
    this as the time interval between hitting command-W and return seems
    to matter.

    Here's the relevant code from my NSDocument:

    - (IBAction)saveAllFiles:(id)sender;
    {
    NSLog(@"%p %s",self,__func__);
    for (ESNode *node in self.projectFiles) {
      if (node.isEdited) {
      NSError *writeError;
      if (![node writeBodyToFileError:&writeError]) {
        NSLog(@"%p %s %@",self,__func__,writeError);
        [self presentError:writeError];
      }
      }
    }

    [self saveDocument:nil];
    }

    - (void)unsavedFilesAlertDidEnd:(NSAlert *)alert returnCode:
    (NSUInteger)returnCode contextInfo:(void *)contextInfo;
    {
    [alert release];

    if (returnCode == NSAlertSecondButtonReturn)
      return;
    if (returnCode == NSAlertFirstButtonReturn) {
      [self saveAllFiles:nil];
      [self close];
    }
    if (returnCode == NSAlertThirdButtonReturn)
      [self close]; // close without saving
    }

    - (void)canCloseDocumentWithDelegate:(id)delegate shouldCloseSelector:
    (SEL)shouldCloseSelector contextInfo:(void *)contextInfo;
    {
    NSLog(@"%p %s",self,__func__);
    BOOL canClose = YES;

    for (ESNode *node in self.projectFiles) {
      if (node.isEdited) {
      canClose = NO;
      break;
      }
    }

    if (!canClose) {
      NSAlert *alert = [[NSAlert alloc] init]; // released in didEndSelector
      [alert setMessageText:NSLocalizedString(@"ThereAreUnsavedFiles",@"")];
      [alert
    setInformativeText:NSLocalizedString
    (@"ThereAreUnsavedFileExplanation",@"")];
      [alert addButtonWithTitle:NSLocalizedString(@"SaveAll",@"")];
      [alert addButtonWithTitle:NSLocalizedString(@"Cancel",@"")];
      [alert addButtonWithTitle:NSLocalizedString(@"DontSave",@"")];
      [alert beginSheetModalForWindow:[self windowForSheet]
    modalDelegate:self
    didEndSelector
    :@selector(unsavedFilesAlertDidEnd:returnCode:contextInfo:)
    contextInfo:nil];
    }

    objc_msgSend(delegate,shouldCloseSelector,self,canClose,contextInfo);
    }

    Thanks in advance, this has got be stumped.

    Jon
previous month february 2008 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29    
Go to today