NSFileWrapper,懒加载和保存

13
我有一个基于NSDocument的应用程序,使用文件包装器来保存和加载其数据。文档可以具有各种资源,因此我不想将所有内容加载到内存中。也许我做错了某些事情,但只要我更改了一个(内部)文件,然后保存,我就无法读取任何未加载到内存中的文件。
我已将相关代码分离到一个单独的项目中以重现此行为,并获得相同的结果。基本流程如下:
  • I load an existing document from disk. The main fileWrapper is a directory filewrapper (I'll call that main) containing two other filewrappers (sub1 and sub2). The two inner filewrappers are not loaded at this point.
  • When the user wants to edit sub1, it is loaded from disk.
  • The user saves the document
  • If the user wants to edit the other file (sub2), it cannot load. The error that appears:

    -[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.
    

这是我项目中相关的代码:

您可以在此Gist中更轻松地阅读此代码: 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];
}

底部的两种方法仅用于更新UI,以便我能看到发生了什么。
- 要更改文件的内容,我会删除现有的fileWrapper并添加新的fileWrapper。这是我找到的唯一更改文件内容的方法,也是我在其他SO答案中看到的方法。 - 当从磁盘加载文档时,我保留fileWrapper,以便我可以使用它(在上面的代码中称为myWrapper)。
苹果文档表示,NSFileWrapper支持惰性加载和增量保存,因此我假定我的代码存在某些根本性缺陷,但我无法看到。

你的文档支持版本控制吗? - user557219
我不确定,所以我认为不会 :) 我还没有做任何支持它的事情。那会改变什么吗?我稍后会研究一下。 - Bob Vork
1
由于我正在使用NSDocument设置,因此我支持版本控制。我尝试关闭它,但结果是相同的。 - Bob Vork
你尝试过访问官方的苹果开发者论坛吗?那里有经验丰富的人,我个人认为。 - SevenBits
@SevenBits IMAO代表什么? - RyanR
显示剩余3条评论
3个回答

1
在您的-fileWrapperOfType:error:方法中,尝试构建一个新的文件包装器,其中包含更改成员的新内容,并引用旧的文件包装器以保留未更改的成员。

1
一个NSFileWrapper本质上是一个unix文件节点的包装器。如果文件被移动,包装器仍然有效。
你似乎遇到了问题,即在保存期间创建新的文件包装器是一个新的文件夹。系统会删除您先前的包装器,包括sub2
要实现您想要的效果,您需要改为增量保存,即仅在原地保存更改的部分。请参见NSDocument中的“原地保存”。

0

按照文档中的说明使用addFileWrapper:添加子目录,意味着

directory/

  1. addfileWrapper:fileName1 directory/fileName1/

  2. addfileWrapper:fileName2 directory/fileName1/fileName2。 该文件不存在。

你必须使用addRegularFileWithContents:preferredFilename:代替。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接