Подтвердить что ты не робот

NSFileWrapper, ленивая загрузка и сохранение

У меня есть приложение на основе NSDocument, которое использует файловые завесы для сохранения и загрузки своих данных. Документ может иметь всевозможные ресурсы, поэтому я не хочу загружать все в память. Я мог бы сделать что-то принципиально неправильное, но как только я сменил один (внутренний) файл, а затем сохранил, я не могу прочитать файл, который не был загружен в память.

Я разделил соответствующий код на отдельный проект, чтобы воспроизвести это поведение, и получаю те же результаты. Основной поток таков:

  • Загружаю существующий документ с диска. Основной файл файл является файловым файлом каталога (я буду называть его основным), содержащим два других файла файла (sub1 и sub2). В этот момент загружаются два внутренних файла файла не.
  • Когда пользователь хочет редактировать sub1, он загружается с диска.
  • Пользователь сохраняет документ
  • Если пользователь хочет отредактировать другой файл (sub2), он не может загрузить. Появится сообщение об ошибке:

    -[NSFileWrapper regularFileContents] tried to read the file wrapper contents lazily but an error occurred: The file couldn’t be opened because it doesn’t exist.
    

Вот соответствующий код в моем проекте:

Этот код может быть проще прочитать в этом контексте: https://gist.github.com/bob-codingdutchmen/6869871

#define FileName01 @"testfile1.txt"
#define FileName02 @"testfile2.txt"

/**
 *  Only called when initializing a NEW document
 */
-(id)initWithType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
    self = [self init];
    if (self) {
        self.myWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];

        NSLog(@"Initializing new document...");

        NSString *testString1 = @"Lorem ipsum first sub file";
        NSString *testString2 = @"This is the second sub file with completely unrelated contents";

        NSFileWrapper *w1 = [[NSFileWrapper alloc] initRegularFileWithContents:[testString1 dataUsingEncoding:NSUTF8StringEncoding]];
        NSFileWrapper *w2 = [[NSFileWrapper alloc] initRegularFileWithContents:[testString2 dataUsingEncoding:NSUTF8StringEncoding]];

        w1.preferredFilename = FileName01;
        w2.preferredFilename = FileName02;

        [self.myWrapper addFileWrapper:w1];
        [self.myWrapper addFileWrapper:w2];

    }
    return self;
}

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

    // This obviously wouldn't happen here normally, but it illustrates
    // how the contents of the first file would be replaced
    NSFileWrapper *w1 = [self.myWrapper.fileWrappers objectForKey:FileName01];
    [self.myWrapper removeFileWrapper:w1];

    NSFileWrapper *new1 = [[NSFileWrapper alloc] initRegularFileWithContents:[@"New file contents" dataUsingEncoding:NSUTF8StringEncoding]];
    new1.preferredFilename = FileName01;

    [self.myWrapper addFileWrapper:new1];

    return self.myWrapper;
}

-(BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
    self.myWrapper = fileWrapper;
    return YES;
}

- (IBAction)button1Pressed:(id)sender {
    // Read from file1 and show result in field1
    NSFileWrapper *w1 = [[self.myWrapper fileWrappers] objectForKey:FileName01];
    NSString *string1 = [[NSString alloc] initWithData:w1.regularFileContents encoding:NSUTF8StringEncoding];
    [self.field1 setStringValue:string1];
}

- (IBAction)button2Pressed:(id)sender {
    // Read from file2 and show result in field2
    NSFileWrapper *w2 = [[self.myWrapper fileWrappers] objectForKey:FileName02];
    NSString *string2 = [[NSString alloc] initWithData:w2.regularFileContents encoding:NSUTF8StringEncoding];
    [self.field2 setStringValue:string2];
}

Нижние два метода предназначены только для обновления пользовательского интерфейса, чтобы я мог видеть, что происходит.

  • Чтобы изменить содержимое файла, я удаляю существующий fileWrapper и добавляю новый. Это единственный способ изменить содержимое файла и способ, которым я видел его в других ответах SO.

  • Когда документ загружается с диска, я держу файловый обозреватель, поэтому я могу его использовать (он называется myWrapper в коде выше)

Документы Apple говорят, что NSFileWrapper поддерживает ленивую загрузку и добавочную экономию, поэтому я предполагаю, что у моего кода есть некоторые фундаментальные недостатки, которые я не вижу.

4b9b3361

Ответ 1

NSFileWrapper по существу является оберткой файла unix node. Если файл перемещен, оболочка остается действительной.

Проблема заключается в том, что создание новой файловой оболочки во время сохранения - это новая папка. И система удаляет вашу предыдущую оболочку, включая sub2.

Чтобы достичь того, что вы хотите, вам нужно изменить на инкрементное сохранение, т.е. сохранить только измененные части на месте. См. "Сохранение на месте" в NSDocument.

Ответ 2

В вашем методе -fileWrapperOfType:error: попробуйте создать новую оболочку файла, которая имеет новое содержимое для измененных членов и ссылается на старые обертки файлов для неизмененных членов.

Ответ 3

Следуя документации addFileWrapper: вы добавляете к нему дочерний (подкаталог), значит

Каталог/

  • addfileWrapper: имя_файла1 Каталог/имя_файла1/

  • addfileWrapper: filename2 Каталог/имя_файла1/filename2. Этот файл не существует.

Вы должны использовать addRegularFileWithContents: preferredFilename: вместо этого.