经过几周的测试和学习什么有效,什么无效,我简化了我的 UIDocument 合并代码。我做出的错误假设之一是需要将UIDocument的revertToContentsOfURL:作为解析过程的一部分。这是一个非常不稳定的 API 调用,我发现最好避免,即使在@try()中使用它也不能防止不必要的崩溃。这让我删除它只是为了看看会发生什么,如果没有它,冲突就会很好地解决。在 developer.apple.com 上有文档冲突解决的示例代码,暗示应该使用它。它似乎在 WWDC2018 之后消失了。
剩下的唯一问题是,如果您有 2 个设备同时打开,您可能会进入竞争状态,因为它们都在不断地合并文档。
尽管文档被标记为冲突,但我之前经历过零冲突版本,但最近我没有看到这种情况发生。一定是我之前做错了什么。我将代码保留在那里,因为它没有害处。
我认为在这里值得一提的另一个问题是,如果您是 UIDocument 的新手,请记住它是 UIKit 的一部分,您需要确保在主线程上完成更新。我发现这个有用的技巧解决了我仍然遇到的一些剩余问题。
- (void) foobar {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleDocumentStateChange:)
name:UIDocumentStateChangedNotification
object:_myDocument];
}
- (void) handleDocumentStateChange: (NSNotification *) notification {
if (_myDocument.documentState & UIDocumentStateInConflict) {
if (_resolvingConflicts) {
return;
}
NSArray *conflictVersions = [NSFileVersion unresolvedConflictVersionsOfItemAtURL:_myDocument.fileURL];
if ([conflictVersions count] == 0) {
return;
}
NSMutableArray *docs = [NSMutableArray new];
[docsData addObject:_myDocument.data]; // Current document data
_resolvingConflicts = YES;
for (NSFileVersion *conflictVersion in conflictVersions) {
MyDocument *myDoc = [[MyDocument alloc] initWithFileURL:conflictVersion.URL];
NSError *error;
[myDoc readFromURL:conflictVersion.URL error:&error];
if ((error == Nil) && (myDoc.data != Nil)) {
[docs addObject:myDoc.data];
}
}
if ([self mergeDocuments:docs]) {
[self saveChangesToDocument];
}
for (NSFileVersion *fileVersion in conflictVersions) {
fileVersion.resolved = YES;
}
[self deleteiCloudConflictVersionsOfFile:_myDocument.fileURL
completion:^(BOOL success){
self.resolvingConflicts = NO;
dispatch_async(dispatch_get_main_queue(), ^{
// On main thread for UI updates
[[NSNotificationCenter defaultCenter] postNotificationName:kMyDocsUpdateNotification object:nil];
});
}];
}
}
- (void) deleteiCloudConflictVersionsOfFile : (NSURL *) fileURL {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fileCoordinator coordinateWritingItemAtURL:fileURL
options:NSFileCoordinatorWritingForDeleting
error:nil
byAccessor:^(NSURL* writingURL) {
NSError *error;
if ([NSFileVersion removeOtherVersionsOfItemAtURL:writingURL error:&error]) {
NSLog(@"deleteiCloudConflictVersionsOfFile: success");
} else {
NSLog(@"deleteiCloudConflictVersionsOfFile: error; %@", [error description]);
}
}];
});
}