检测跨设备删除 iCloud 中文档的正确方法?

Posted

技术标签:

【中文标题】检测跨设备删除 iCloud 中文档的正确方法?【英文标题】:Proper way to detect deletion of documents in iCloud across devices? 【发布时间】:2012-05-02 01:48:32 【问题描述】:

我找不到一种明显的方法来检测我正在监视的文档的删除(我缓存了我的应用关心的文档列表)。

在设备 1 上: 当我删除一个文档时,我会调用

        [fileCoordinator coordinateWritingItemAtURL:fileURL options:NSFileCoordinatorWritingForDeleting
          error:&err byAccessor:^(NSURL* writingURL) 
             [fileManager removeItemAtURL:writingURL error:nil];

这在 Device1 上运行良好,一切都保持同步。 在设备 2 上: 我试图依靠NSMetadataQuery 通知:

NSMetadataQueryDidFinishGatheringNotification 上的初始文件列表正常 通过NSMetadataQueryDidUpdateNotification

可以很好地添加文档/更改

当我删除 Device1 上的文件时,我得到了一个奇怪的结果:NSMetadataQueryDidUpdateNotification 中出现了更新,其中列出了我的所有文档(除了已删除的文档) 我不确定我应该如何检测丢失的文件已被删除或更新通知是为此目的

问题 1:我应该检查什么?

我尝试了另一条路线,即为 iCloud URL 注册为 NSFilePresenter

- (NSURL *)iCloudDocumentsURL

    return [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] URLByAppendingPathComponent:@"Documents"];

现在,当该 URL 中的文件发生更改时,我会通过 NSFilePresenter 协议进行调用,但是由于回调是模糊的,因此运行逻辑以确定丢失的文档的速度非常慢。 我唯一接到的电话是- (void)presentedItemDidChange; 我期待通过- (void)accommodatePresentedSubitemDeletionAtURL:(NSURL *)url completionHandler:(void (^)(NSError *errorOrNil))completionHandler 得到回调 但是这个方法永远不会被调用。

问题 2:知道如何让它发挥作用吗?

【问题讨论】:

【参考方案1】:

由于您正在跟踪以前从 iCloud 报告的文件,因此 当前 iCloud 列表中不存在已删除的文件,因此您只需将两个列表与找出哪个被删除了。

这就是我在“文件管理器”视图控制器中所做的,因为我保留了一个文件条目的 NSMutableDictionary,其中包括在我的主视图中文件“图标”位置的键。当我收到更新通知并且该更新导致文件更多或更少时,我会根据这些文件对图标更改进行动画处理 变化。

【讨论】:

这样您就不会知道文件何时被移动。如果你不太在意,把元数据存储在文件包中,没关系,但是如果你在外面引用文件,那么这种方法就不会产生很好的效果。【参考方案2】:

tobinjim 是正确的,因为 NSMetadataQuery 每次都返回整个结果集。我原以为只发送更改以节省带宽,但我没有正确地 RTFM。

当我弄清楚这一点后,我在 ios 库中遇到了一个错误。这是当我在一台设备上删除文档并且 iCloud 查询更新遇到我的另一台设备时发生的崩溃:

    2012-06-25 13:15:12.343 app[19779:707] *** -[NSMutableIndexSet indexGreaterThanOrEqualToIndex:]: message sent to deallocated instance 0xdaae2c0
    (gdb) bt
    #0  0x31937870 in ___forwarding___ ()
    #1  0x31892650 in __forwarding_prep_0___ ()
    #2  0x373cc676 in __NSIndexSetEnumerate ()
    #3  0x373a1ee8 in -[NSIndexSet enumerateIndexesWithOptions:usingBlock:] ()
    #4  0x371c1f08 in -[LBQuery _processUpdates] ()
    #5  0x373571a6 in -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] ()
    #6  0x3737ffa4 in -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:] ()
    #7  0x371c2274 in -[LBQuery processUpdates] ()
    #8  0x373a88d6 in -[NSMetadataQuery _update] ()

我相信我已经找到了这个问题的原因。我正在从 Luke the Hiesterman 的演示文稿中查看新的 IOS 6 示例代码。我注意到没有像以前的 iCloud 示例应用程序那样调用 NSMetadataQuery disable/enableUpdates。我删除了对这些的所有调用。我还更改了处理 NSMetadataQueryDidUpdateNotification 调用的方法以异步但以排队方式运行。

NSMetadataQuery 的线程之间似乎出现了竞争条件。在某些调用中,查询的结果很好,但在其他时候,它们已被释放并且结果显示为 NSZombie 的(感谢 gdb,它比当前的 lldb 好得多)。所以跨线程启用和禁用查询会导致 LBQuery 通过调用已释放的对象而崩溃。

取消所有对 NSMetadataQuery 的启用和禁用似乎也加快了我的应用程序的速度,而且 iCloud 似乎更加稳定。

【讨论】:

确实很有趣。您现在是否像 NSMetaDataQueryDidFinishGatheringNotification 的观察者一样将自己移除以像停止查询一样停止中断? 我没有。我的回调方法会触发处理异步,因此它不会减慢主线程。我确实必须在变量周围添加一些 @synchronized 块,这些变量可能会被多个异步线程更改。现在稳定快速,没有崩溃【参考方案3】:

从 iOS 8 开始,NSMetadataQuery 扩展了 NSMetadataQueryDidUpdateNotification userInfo 和 NSMetadataQueryUpdateRemovedItemsKey,您可以使用它来检测哪些文件被删除了。

https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSMetadataQuery_Class/#//apple_ref/doc/constant_group/Keys_for_Use_with_a_Notification_Info_Dictionary

【讨论】:

以上是关于检测跨设备删除 iCloud 中文档的正确方法?的主要内容,如果未能解决你的问题,请参考以下文章

正确处理在您的应用程序的另一个实例中使用 iCloud 删除 UIDocument

删除支持 iCloud 的文件夹中包含文档的 iOS 应用程序

iCloud NSUserDefaults 未跨设备同步(通过 MKiCloudSync)

icloudsdownload里的文件无法查看

是否可以跨多个应用程序(相同的 id)访问 iCloud 文档数据,还是沙盒?

有啥方法可以通过 iCloud Drive 支持最近删除的文件?