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

标签 ios thread-safety save uidocument nsfilewrapper

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

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

UIDocument crash stack trace

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

- (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 本身就是目录文件包装器时,情况是否有所不同?如果认可的方法是错误的,应该怎么做?

最佳答案

如果您正在调用任何 writeContents:... 方法,则不应如此。您应该改为调用 saveToURL:forSaveOperation:completionHandler:writeContents:... 方法用于高级子类化。

UIDocument 使用两个线程 - 主线程和“UIDocument 文件访问”线程(如果您子类化更多 UIDocument,您可以通过 performAsynchronousFileAccessUsingBlock:).

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

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

保存时,您通常希望让 UIDocument 决定何时执行此操作,并通过 updateChangeCount: 方法让它知道某些内容已更改(参数为 UIDocumentChangeDone )。当您想立即保存某些内容时,您需要使用 saveToURL:forSaveOperation:completionHandler: 方法。

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

关于ios - UIDocument 和 NSFileWrapper - NSFastEnumerationMutationHandler 在保存期间更改文件包装器时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15232287/

相关文章:

.net - 类实例在单个线程中被调用和销毁时是否是线程安全的?

matlab - 将终端下的Matlab不可见图保存为相同大小的图像

java - 保存/更新具有多对多关系的 Hibernate 实体

ios - 上传 iPhone 应用程序供审核,无需应用内购买

ios - 有没有人在 iOS flutter 上看到过 stripe_payments 的这个编译错误?

objective-c - 为捏合手势设置 UIScrollView 的原点

c# - TryGetValue 线程本身是否安全

ios - 带有 iOS 6 AVAudioPlayer 的 iPhone 4S 正在工作,但没有声音

java - 使用 CachedThreadPool 写入 JTextArea

android - onSaveInstanceState 的目的