我有一个基于 UIDocument
的应用程序,它使用 NSFileWrapper
来存储数据。 “主”文件包装器包含许多其他目录文件包装器,每个包装器代表文档的不同页面。
每当我在保存 UIDocument
时更改文档(在 writeContents:andAttributes:safelyToURL:forSaveOperation:error:
中),应用程序就会崩溃。这是堆栈跟踪:
很明显,我正在修改 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) 另一个对象或应用程序正在读取/写入同一文件,您只需要担心编写自己的 NSFilePresenter
。 UIDocument
已经完成的工作会很好。但是,您确实希望在移动/删除整个文档时使用 NSFileCoordinator
。
关于ios - UIDocument 和 NSFileWrapper - NSFastEnumerationMutationHandler 在保存期间更改文件包装器时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15232287/