2013-10-07 3 views
13

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

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

  • Загружаю существующий документ с диска. Основной файл-файл является файловым файлом каталога (я буду называть это основным), содержащим два других файлообмена (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]; 
} 

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

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

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

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

+0

Поддерживает ли ваш документ управление версиями? –

+0

Я не уверен, поэтому я не думаю :) Я не сделал ничего, чтобы поддержать его. Это изменит что-нибудь? Я рассмотрю это позже. –

+1

Поскольку я использую установку NSDocument, я поддерживал управление версиями. Я пытался отключить его, но результат тот же –

ответ

1

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

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

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

1

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

0

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

каталог/

  1. addfileWrapper: имя_файла1 каталог/имя_файла1/

  2. addfileWrapper: filename2 directory/fileName1/fileName2. Этот файл не существует.

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

Смежные вопросы