NSDocument packages and incremental writing using NSFileWrapper
NSDocument packages and incremental writing using NSFileWrapper
- Subject: NSDocument packages and incremental writing using NSFileWrapper
- From: Thomas Zoechling <email@hidden>
- Date: Wed, 30 Jan 2013 16:53:35 +0100
Hello,
My NSDocument based app uses packages with the following structure:
- document.package
+- metadata.plist (small, mutable)
+- large0.file (large, immutable)
+- large1.file (large, immutable)
+- large2.file (large, immutable)
While the metadata.plist file can change any time, all large files are created once and then stay as they are.
As my "large0-2" files can be several gigabytes, I try to carefully avoid unnecessary IO by using document packages with NSFileWrapper.
My package reading/writing methods resemble the ones in the "Document Package with iCloud" sample (But I don't use iCloud): http://developer.apple.com/library/mac/#samplecode/PackagedDocument/
I appended the relevant parts of my code at the end of this message.
With this setup, I was hoping that the occasions where the document architecture has to copy the whole bundle are reduced to:
- File duplication
- Moves to another volume
But after investigating file activity with Instruments.app, it turned out that my app is reading and writing chunks of my unmodified, immutable & large files during each save.
It seems that the copying is related to document revisions (a.k.a. Versions).
Instruments logs hundreds of IO operations in the form of:
# Caller Function FD Path Bytes
...
70 copyfile read 22 document.package/large0.file 1048576
71 copyfile write 23 /.vol/16777218/2/.DocumentRevisions-V100/staging/adding.Wohcjo4i/4772FAAA-78D3-44A9-9412-A2D651B6EB5A.package/large0.file 1048576
70 copyfile read 22 document.package/large0.file 1048576
71 copyfile write 23 /.vol/16777218/2/.DocumentRevisions-V100/staging/adding.Wohcjo4i/4772FAAA-78D3-44A9-9412-A2D651B6EB5A.package/large0.file 1048576
70 copyfile read 22 document.package/large0.file 1048576
71 copyfile write 23 /.vol/16777218/2/.DocumentRevisions-V100/staging/adding.Wohcjo4i/4772FAAA-78D3-44A9-9412-A2D651B6EB5A.package/large0.file 1048576
...
How can I tell Versions that the only file that constantly changes within my document.package is metadata.info? (Without turning off document revisions altogether)
I am targetting 10.8 (with sandboxing enabled) and also adopted async saving and autosavesInPlace.
with kind regards,
Thomas
---
- (NSFileWrapper*)fileWrapperOfType:(NSString*)typeName error:(NSError**)outError
{
if([self documentFileWrapper] == nil)
{
NSFileWrapper* documentFileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
[self setDocumentFileWrapper:documentFileWrapper];
[documentFileWrapper release];
}
NSURL* documentURL = [self fileURL] == nil ? [self autosavedContentsFileURL] : [self fileURL];
if(documentURL)
{
//Check if we already have a metadata file wrapper. If "YES" remove it.
NSFileWrapper* previousRecordingInfoFileWrapper = [[[self documentFileWrapper] fileWrappers] objectForKey:kSSWInfoPlistFilename];
if(previousRecordingInfoFileWrapper != nil)
{
[[self documentFileWrapper] removeFileWrapper:previousRecordingInfoFileWrapper];
}
NSData* recordingInfoData = [NSPropertyListSerialization dataFromPropertyList:recordingInfoDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&serializationErrorDescriptions];
[self unblockUserInteraction];
NSFileWrapper* recordingInfoFileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:recordingInfoData];
[recordingInfoFileWrapper setPreferredFilename:kSSWInfoPlistFilename];
[[self documentFileWrapper] addFileWrapper:recordingInfoFileWrapper];
[recordingInfoFileWrapper release];
if(([[[self documentFileWrapper] fileWrappers] objectForKey:kSSWRecordingFrameName] == nil))
{
NSFileWrapper* frameFileWrapper = [[NSFileWrapper alloc] initWithURL:[documentURL URLByAppendingPathComponent:kSSWRecordingFrameName] options:0 error:outError];
[frameFileWrapper setPreferredFilename:kSSWRecordingFrameName];
[[self documentFileWrapper] addFileWrapper:frameFileWrapper];
[frameFileWrapper release];
}
...
}
}
- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError **)outError
{
BOOL didRead = NO;
NSFileWrapper* recordingInfoWrapper = [[fileWrapper fileWrappers] objectForKey:kSSWInfoPlistFilename];
if(!recordingInfoWrapper)
{
NSMutableDictionary* errorDetail = [NSMutableDictionary dictionary];
NSString* errorDescription = NSLocalizedString(@"Could not open recording.\nThe recording is missing it's metadata file.", @"Displayed when trying to open a corrupted bundle");
[errorDetail setValue:errorDescription forKey:NSLocalizedDescriptionKey];
if(outError)
{
*outError = [NSError errorWithDomain:kSSWRecordingErrorDomain code:100 userInfo:errorDetail];
}
}
else
{
NSDictionary* recordingInfoDict = [NSPropertyListSerialization propertyListWithData:[recordingInfoWrapper regularFileContents] options:0 format:nil error:outError];
if(recordingInfoDict)
{
self.recordingInfo = recordingInfoDict;
}
}
[self setDocumentFileWrapper:fileWrapper];
return didRead;
}
_______________________________________________
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