How to save a document without blocking the main thread
How to save a document without blocking the main thread
- Subject: How to save a document without blocking the main thread
- From: Antonio Nunes <email@hidden>
- Date: Thu, 20 Dec 2007 14:51:36 +0000
Hi,
I'm hoping third time is lucky, because I have a hard time imagining
absolutely no-one on this list would have any light to shed on this
matter. (Not that anybody _has_ to of course, even if they do :-) But
maybe I have not been very successful at presenting the situation in a
clear manner.
So, the issue is: in the project I'm working on saving a file to disk
can be a particularly lengthy operation. If I save a document's state
to disk using the usual methods this can, even on the latest hardware,
result in an unresponsive app with a spinning beach-ball for an
extended period of time (many seconds, minutes even if the files are
very large).
I need to therefore write out the document in a separate thread. To
bloc changes to the document at hand, and provide feedback, a modal
sheet is presented that reflects the saving progress, but leaves the
runloop free to respond to user actions thus maintaining a healthy
feel to the app and allowing the user to work on other documents.
I have no problems saving the file. That all happens correctly. The
problem is that after the file is saved and worked on some more, then,
when trying to write the modifications out to disk NSDocument (I
suppose) complains that the file has been modified by another
application.
I think the reason for this is: The initial part of the saving process
is done through the usual channels provided by NSDocument: If the
document is untitled a Save Panel appears, if not the routine to write
out the data is called straight away. This is where I fork a thread
that does the actual saving. However: after forking the thread the
method returns immediately and, *I suppose*, the framework's saving
mechanism at that point believes a successful save operation has
completed and records some state info about the doc's on disk
representation (like time of saving). Then when a new save command is
issued by the user the document checks its on disk representation
state and compares it to its internal data and discovers that the last
modification has a later date the one stored in memory. So the doc
must have been changed by another app and the user is alerted. [This
is conjecture on my part, but seems to me the most likely scenario.]
What I need then is a way to synchronise the internally recorded state
info with the on disk representation after I am done writing out the
data. Is there a way to do this? Or do I need to take the whole
process into my own hands right from the start, bypassing the
NSDocument's saving services altogether? I'd rather not, if I don't
really have to.
I hope this makes cear what is going on, and some help, any pointers,
on this matter would be really appreciated.
The method where I hook into the saving mechanism and fork the thread
is the following:
- (void)saveToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName
forSaveOperation:(NSSaveOperationType)saveOperation delegate:
(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void
*)contextInfo
{
[NSApplication detachDrawingThread:@selector(saveDocumentToDisk:)
toTarget:self withObject:[NSDictionary
dictionaryWithObjectsAndKeys:absoluteURL, @"absoluteURL", typeName,
@"typeName", [NSNumber numberWithInteger:saveOperation],
@"saveOperation", nil]];
}
The relevant parts of the save method, as a method in the NSDocument
subclass:
- (void)saveDocumentToDisk:(NSDictionary *)info
{
NSURL *absoluteURL = [info objectForKey:@"absoluteURL"];
NSString *path = [absoluteURL path];
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
success = [[NSFileManager defaultManager] createDirectoryAtPath:path
withIntermediateDirectories:YES attributes:nil error:NULL];
}
if (success) {
[...save state...everything is written into the package...]
}
// Freshly saved state: on disk rep is synched with interal rep
if (success) {
[self updateChangeCount:NSChangeCleared];
}
}
This is what I have now, it almost works, but not quite well enough.
[All on Leopard in a garbage collected app by the way.]
Cheers,
António
-----------------------------------------------
Touch is a language without words
-----------------------------------------------
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden