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

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

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

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

    -[NSFileWrapper regularFileContents] tried to read the file wrapper's 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];
}

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

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

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

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


person Bob Vork    schedule 07.10.2013    source источник
comment
Поддерживает ли ваш документ управление версиями?   -  person    schedule 07.10.2013
comment
Я не уверен, поэтому думаю, что нет :) Я ничего не сделал для его поддержки. Изменит ли это что-нибудь? Я посмотрю на это позже   -  person Bob Vork    schedule 07.10.2013
comment
Поскольку я использую установку NSDocument, я поддерживал управление версиями. Пробовал отключать, результат тот же   -  person Bob Vork    schedule 10.10.2013
comment
Вы пробовали заходить на официальные форумы разработчиков Apple? Вы получите опытных людей там ИМАО.   -  person SevenBits    schedule 12.10.2013
comment
@SevenBits Что означает IMAO?   -  person RyanR    schedule 17.10.2013
comment
Я столкнулся с точно такой же проблемой - любопытно узнать, придумали ли вы когда-нибудь, как это исправить.   -  person jlong64    schedule 17.09.2014
comment
Вы когда-нибудь находили решение? Начиная с macOS High Sierra, мое приложение теперь показывает ту же ошибку. Раньше работало нормально...   -  person Mark    schedule 19.09.2017
comment
С тех пор я перешел от разработки приложений для Mac и, к сожалению, так и не нашел решения для этого...   -  person Bob Vork    schedule 20.09.2017


Ответы (3)


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

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

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

person Cocoanetics    schedule 11.11.2013

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

person tjw    schedule 14.01.2014

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

каталог/

  1. addfileWrapper:fileName1 каталог/fileName1/

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

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

person macrene    schedule 26.03.2014