Ios UID文件及;NSFileWrapper—大型文件需要很长时间才能保存,尽管有增量更改

Ios UID文件及;NSFileWrapper—大型文件需要很长时间才能保存,尽管有增量更改,ios,save,uidocument,nsfilewrapper,Ios,Save,Uidocument,Nsfilewrapper,我有一个基于UIDocument的应用程序,它使用NSFileWrappers来存储数据。“主”文件包装包含许多额外的目录文件包装,每个包装代表文档的不同页面 当保存一个只修改了一小部分页面的大型文档时,UIDocument会在后台花费很长时间编写更改(在writeContents:andAttributes:safelytoul:forSaveOperation:error:)。当然,它应该只写下对文件包装器的一个小改动。。。怎么花了这么长时间 MycontentsForType:错误:ove

我有一个基于
UIDocument
的应用程序,它使用
NSFileWrapper
s来存储数据。“主”文件包装包含许多额外的目录文件包装,每个包装代表文档的不同页面

当保存一个只修改了一小部分页面的大型文档时,
UIDocument
会在后台花费很长时间编写更改(在
writeContents:andAttributes:safelytoul:forSaveOperation:error:
)。当然,它应该只写下对文件包装器的一个小改动。。。怎么花了这么长时间

My
contentsForType:错误:
override返回一个新的目录文件包装,其中包含主文件包装的内容(a la):

下面是来自时间分析器的堆栈跟踪的可爱图片:

顺便说一句,它说要在工作线程中保存~1.6秒—在实际运行时间中,这相当于大约8秒


编辑:

有什么方法可以检查文件包装器是否需要写入磁盘?这样我就可以确认我没有做一些奇怪的事情,比如当我做一个小的更改时更新每个子文件包装器(尽管我确定我没有…)


编辑:

我进一步研究了CloudNotes示例应用程序,它似乎实现了增量保存,至少在这种情况下是这样的!我通过初始化一个包含100个注释的文档来测试它,每个注释包含大约5MB的数据。我在这里和那里做了一个小的编辑(对文本视图的单个字符更改会将文档标记为需要保存),并大致记录了每次保存所用的时间。测试相对粗糙(在模拟器上运行),但结果如下:

  • 第一次写入:~8000ms
  • 第二次写入:~4000ms
  • 第三次写入:~300ms
  • 所有后续写入:~40ms
显然,有许多因素会影响所需的时间,特别是因为它是在后台线程中使用文件协调进行保存的,但一般来说,趋势似乎总是呈指数衰减,直到所有写入都变得非常快


但我仍在试图弄清楚为什么我的应用程序中不会出现这种情况。对于一个大的多页文档(虽然很大,但仍然比我上面执行的CloudNotes测试的文档小很多倍),用户可能需要等待几秒钟才能关闭文档。我不想为实际上应该是即时的东西设置微调器。

NSFileWrapper
实际上正在将整个文档加载到内存中。因此,对于
UIDocument
,使用
NSFileWrapper
实际上不适合大型文档。文档使您认为它可以进行增量保存,但在我的例子中,它似乎没有这样做

UIDocument
不仅仅限于
NSFileWrapper
NSData
。您可以使用自己的自定义类,只需重写某些方法。我最终编写了自己的文件包装器类,该类只引用磁盘上的文件,并按需读取/写入单个文件

这就是我的
UIDocument
类使用自定义文件包装器时的样子:

@implementation LSDocument

- (BOOL)writeContents:(LSFileWrapper *)contents
        andAttributes:(NSDictionary *)additionalFileAttributes
          safelyToURL:(NSURL *)url
     forSaveOperation:(UIDocumentSaveOperation)saveOperation
                error:(NSError *__autoreleasing *)outError
{
    return [contents writeUpdatesToURL:self.fileURL error:outError];
}

- (BOOL)readFromURL:(NSURL *)url error:(NSError *__autoreleasing *)outError
{
    __block LSFileWrapper *wrapper = [[LSFileWrapper alloc] initWithURL:url isDirectory:NO];
    __block BOOL result;
    dispatch_sync(dispatch_get_main_queue(), ^(void) {
        result = [self loadFromContents:wrapper
                                 ofType:self.fileType
                                  error:outError];
    });
    [wrapper loadCache];
    return result;
}

@end

我将其用作基类,并将其子类化用于其他项目。它应该让您了解如何集成自定义文件包装器类。

我知道这是一个非常古老的线程,但为了帮助未来的旅行者:在我的例子中,我有一个子目录NSFileWrapper,它没有增量保存

我发现,如果要复制NSFileWrapper,则需要将副本的文件名、文件属性(可能还有preferredFilename)设置为原始文件名,以便增量保存。复制完这些文件后,子文件夹的内容将以增量方式保存(即,仅当替换为新的NSFileWrappers时才写入)


苹果注意:说真的,整个NSFileWrapper API乱七八糟,应该清理干净。

感谢您花时间回答(我的两个相关问题!)。我花了很多时间试图研究NSFileWrapper的细节,但是关于它们的信息太少了。有很多事情让我困惑:1)如果文件包装器将所有数据加载到内存中,并且不支持增量保存,那么它们有什么意义?2) 文档不仅仅是“让你觉得”他们做的是增量储蓄,它直截了当地说明了这一点!来自“iOS基于文档的应用程序编程指南”:“文件包装器支持增量保存。与单个二进制数据对象不同,如果文件包装器包含您的文档数据(例如,文本和图像),并且文本发生更改,则只需将包含文本的文件写入磁盘。此功能可提高性能。“文件NSFileWrapper是不可变的,因此我认为它可能会保存已替换的文件包装。因此,如果您更换NSFileWrapper,即使它已更改,也可能会保存它。NSFileWrapper将所有内容加载到内存中,因此在我的情况下,这是不可取的-因此我制作了我自己的,它引用文件,而不是w/a BOOL,以指示我希望在内存中缓存某些文件。当我更新一个文件时,它会用一个“更新”的布尔值设置一个“内容”ivar。保存我的父项时,它会查找“已更新”并保存更新的文件,如果“缓存”BOOL为false,则会将“内容”设置为零。您的实现听起来不错,但我只想在最后一种手段下偏离NSFileWrapper。我的理解是,定制增量读/写解决方案主要用于文档包含数据块(例如视频文件)的情况。我的文档由许多非常小的文件组成,即使文档的总大小大于100MB,我在读取时也看不到任何性能问题,只是在写入时。这对我来说意味着某种NSFileWrapper优化,但我
@implementation LSDocument

- (BOOL)writeContents:(LSFileWrapper *)contents
        andAttributes:(NSDictionary *)additionalFileAttributes
          safelyToURL:(NSURL *)url
     forSaveOperation:(UIDocumentSaveOperation)saveOperation
                error:(NSError *__autoreleasing *)outError
{
    return [contents writeUpdatesToURL:self.fileURL error:outError];
}

- (BOOL)readFromURL:(NSURL *)url error:(NSError *__autoreleasing *)outError
{
    __block LSFileWrapper *wrapper = [[LSFileWrapper alloc] initWithURL:url isDirectory:NO];
    __block BOOL result;
    dispatch_sync(dispatch_get_main_queue(), ^(void) {
        result = [self loadFromContents:wrapper
                                 ofType:self.fileType
                                  error:outError];
    });
    [wrapper loadCache];
    return result;
}

@end