UIDocument& NSFileWrapper - NSFastEnumerationMutationHandler在保存期间更改文件包装器

时间:2013-03-05 19:19:16

标签: ios thread-safety save uidocument nsfilewrapper

我有一个基于UIDocument的应用,它使用NSFileWrapper来存储数据。 'master'文件包装器包含许多其他目录文件包装器,每个包装器代表文档的不同页面。

每当我在UIDocument保存时(writeContents:andAttributes:safelyToURL:forSaveOperation:error:)对文档进行更改,应用程序崩溃。这是堆栈跟踪:

UIDocument crash stack trace

我似乎很清楚我正在修改UIDocument在后​​台枚举的文件包装器的同一个实例。实际上,我检查了在contentsForType:error:中返回数据模型的快照时,返回的子文件包装器指向与数据模型中当前驻留(和正在编辑)的对象相同的对象,而不是副本。 / p>

- (id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError
{
    if (!_fileWrapper) {
        [self setupEmptyDocument];
    }
    return [[NSFileWrapper alloc] initDirectoryWithFileWrappers:[_fileWrapper fileWrappers]];
}

这是实施此方法的制裁方法(根据WWDC 2012 Session 218 - Using iCloud with UIDocument)。

所以我想问题是:这种方法如何是线程安全的?

当主文件包装器的fileWrappers本身是目录文件包装器时,情况是否有所不同?如果认可的方法是错误的,应该如何呢?

2 个答案:

答案 0 :(得分:6)

如果您正在调用任何writeContents:...方法,则不应该这样做。你应该打电话给saveToURL:forSaveOperation:completionHandler:writeContents:...方法适用于高级子类。

UIDocument使用两个线程 - 主线程和“UIDocument文件访问”线程(如果你继承了UIDocument的更多内容,你可以在via performAsynchronousFileAccessUsingBlock:中执行操作)。

UIDocument的线程安全与Objective C中的任何内容类似 - 只允许拥有对象的线程修改它。如果正在读取要更改的对象,请在写入完成后将其排队以进行更改。也许更改您的UIDocument子类所拥有的其他对象,并将其拉入NSFileWrapper中的新contentsForType:error:。传递fileWrappers NSDictionary的副本。

NSFileWrapper实际上将整个文档加载到内存中。 NSFileWrapper实际上是在readFromURL:error:方法的“UIDocument File Access”线程中创建的,然后传递给loadFromContents:ofType:error:方法。如果你有一个大文件,这可能需要一段时间。

保存时,您通常希望UIDocument决定何时执行此操作,并通过updateChangeCount:方法(param为UIDocumentChangeDone)让其知道某些内容已发生变化。如果您想立即保存 ,则需要使用saveToURL:forSaveOperation:completionHandler:方法。

另外需要注意的是UIDocument实现了NSFilePresenter协议,该协议定义了NSFileCoordinator使用的方法。 UIDocument仅在根文档上协调写入,而不是子文件。您可能认为在文档中协调子文件可能会有所帮助,但是您获得的崩溃与在迭代字典时改变字典有关,因此无法提供帮助。如果您(1)想要获得文件更改通知,或(2)另一个对象或应用正在读/写同一文件,您只需要担心编写自己的NSFilePresenterUIDocument已经完成的工作正常。但是,您确实希望在移动/删除整个文档时使用NSFileCoordinator

答案 1 :(得分:0)

我知道这是一个古老的话题,但是最近我遇到了这个问题,并为将来的旅行者提供了帮助:如果主文件包装器中有子目录,则还需要复制那些NSFileWrappers(除了复制根目录之外)如上所述的NSFileWrapper。

否则,当UIDocument后台线程在保存时枚举它们并在主线程上同时进行修改时,会发生崩溃。尚不清楚,但这可能是OP遇到的问题。

另一个提示是,您还需要复制子目录的NSFileWrapper的文件名,fileAttributes(以及可能的preferredFilename),以便进行增量保存。

HTH。