Frage

So after googling a bit it's my understanding that when I use a bundle for my document in a NSDocument-based app I have to write the entire bundle each time I want to save? This is starting to get problematic here as I'm adding large video files to the document.

Is there any way around this without throwing out NSDocument?

War es hilfreich?

Lösung

One of the advantages of using document packages is that you do not need to write everything each time you save. You should add a documentFileWrapper property to your NSDocument subclass and, when the document architecture reads in a document package, you should store an NSFileWrapper reference in that property, and you should also keep track of what has been changed. For example,aAssuming you’re keeping a text file and a video file in your document package:

@interface MyDocument ()
    @property (nonatomic, copy) NSString *text;
    @property (nonatomic, assign) bool textHasChanged;

    @property (nonatomic, copy) NSData *videoData;
    @property (nonatomic, assign) bool videoHasChanged;

    @property (nonatomic, strong) NSFileWrapper *documentFileWrapper;
@end

and

- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper
                     ofType:(NSString *)typeName
                      error:(NSError **)outError {


    // Read the contents of file wrapper
    // …

    [self setDocumentFileWrapper:fileWrapper];

    return YES;
}

If a document package was read from disk, documentFileWrapper keeps a reference to that package. If it’s a new document, documentFileWrapper is nil.

When saving the document, you reuse documentFileWrapper and write only the files that need saving. In case the document hasn’t been saved yet (it’s a new document), documentFileWrapper is nil, so you need to create one:

- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName
                               error:(NSError **)outError
{

    if ([self documentFileWrapper] == nil) {
        NSFileWrapper *documentFileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:@{}];
        [self setDocumentFileWrapper:documentFileWrapper];
    }

    NSDictionary *fileWrappers = [[self documentFileWrapper] fileWrappers];
    NSFileWrapper *textFileWrapper = [fileWrappers objectForKey:@"text.txt"];
    NSFileWrapper *videoFileWrapper = [fileWrappers objectForKey:@"video.mp4"];

    if ((textFileWrapper == nil && [self text] != nil) || [self textHasChanged]) {
        if (textFileWrapper != nil)
            [documentFileWrapper removeFileWrapper:textFileWrapper];

        if ([self text] != nil) {
            NSData *textData = [[self text] dataUsingEncoding:NSUTF8StringEncoding];
            [documentFileWrapper addRegularFileWithContents:textData preferredFileName:@"text.txt"];
        }
    }

    if ((videoFileWrapper == nil && [self videoData] != nil) || [self videoHasChanged]) {
        if (videoFileWrapper != nil)
            [documentFileWrapper removeFileWrapper:videoFileWrapper];

        if ([self videoData] != nil)
            [documentFileWrapper addRegularFileWithContents:[self videoData] preferredFileName:@"video.mp4"];
    }

    return documentFileWrapper;
}

In the code above, the document package (file wrapper) is created only if it doesn’t already exist. In other words, it is created only if the document is new and hasn’t been saved yet. After the package is created, its reference is stored in documentFileWrapper.

Next, we check whether we need to write the text file. It is written if: * either there is no text file yet (probably because it’s a new package) and we have actual text to write; * or if the text has been changed.

We do the same for the video file.

Note that if the document has been loaded from disk or saved at least once, the video file is added to the package (and the previous video file, if any, is removed from the package) only if the video data has been changed.

NB: this answer assumes ARC and automatic property synthesis (ivar names prefixed with an underscore).

Andere Tipps

I know this has been answered, but as reference, if you take a look at Apple's Packaged Document sample code, you'll see a variant of this that works without having to keep a changed flag for every instance variable.

The basic idea is:

  • If a model variable changes, remove it's corresponding file wrapper from the main file wrapper
  • When building the file wrapper in fileWrapperOfType:… only add those that are missing (i.e. were updated)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top