合并具有相同属性但 ID 不同的 NSManagedObject,与 iCloud 同步会导致重复(Core Data,Swift 1.2)

Posted

技术标签:

【中文标题】合并具有相同属性但 ID 不同的 NSManagedObject,与 iCloud 同步会导致重复(Core Data,Swift 1.2)【英文标题】:Merge NSManagedObject with equal properties, but different IDs, synced with iCloud results in duplicates (Core Data, Swift 1.2) 【发布时间】:2015-04-14 10:56:08 【问题描述】:

我有一个使用 Core Data 和 iCloud 同步的 Swift 1.2 应用程序。

在第一个屏幕中,用户可以插入一些数据来创建自定义的 MyNSManagedObjects

每个 MyNSManagedObject 都必须有一个它所属的特定“组”。 这个“类别”在我的数据模型中由另一个自定义 NSManagedObject 表示,我们称之为 MyManagedObjectsCategory

用户可以创建许多 MyManagedObjectsCategory 对象,但应用还需要一个 MyManagedObjectsCategory 类型的 DEFAULT 对象,以防用户不创建任何不同的 MyManagedObjectsCategory

每个 MyNSManagedObjects 只能有 1 个 MyManagedObjectsCategory,但一个 MyManagedObjectsCategory 可以有多个 MyNSManagedObjects。

当用户启动应用程序时,我会立即检查 DEFAULT MyManagedObjectsCategory 是否已经存在,如果不存在,我会创建一个 DEFAULT MyManagedObjectsCategory 对象并将其保存到持久存储中。当然,只有在应用程序第一次启动时我需要创建这个对象,之后我总是会获取我在应用程序第一次启动时创建的对象并使用它。

我的问题是在我启用 iCloud 同步时开始的;现在,在应用程序首次启动时,会发生这种情况:

    应用启动时按预期使用 STORAGE 1(本地),但未找到 MyManagedObjectsCategory 类型的 DEFAULT 对象,它创建了一个新对象。

    如果有网络覆盖,几秒钟后应用程序切换到 STORAGE 0(云)并保存刚刚创建的 DEFAULT MyManagedObjectsCategory 对象;如果设备离线,这不会立即发生,但当然会在稍后网络连接可用时发生。

当我在不同的设备上第一次启动应用程序时,上面的第 1 点和第 2 点再次发生:由于应用程序从存储 1 开始,它不会获取 DEFAULT MyManagedObjectsCategory 对象它会创建并保存一个新的,几秒钟后或当网络可用时,当应用切换存储时,它会同步到存储 0。

您可以想象,当不同的设备同步时,我发现自己有多个 DEFAULT 对象,而且由于我是 Core Data 的新手,我不知道如何处理这个问题。

一方面,我需要在应用启动时立即使用 DEFAULT 对象,所以我迫不及待地切换到存储 0(另外,因为我不知道用户是否有网络连接,所以存储切换可能会在很久以后发生);另一方面,DEFAULT 对象的目的是在每个设备上都是一个,并且始终相同。

我了解,即使每个 DEFAULT 对象都具有以完全相同的方式创建的匹配属性(对象具有名称和 myID 字符串属性),Core Data 也会为每个托管对象创建一个唯一 ID,并且由于 ID在不同时刻在不同设备上创建的 DEFAULT 对象之间不匹配,它不会将它们合并到单个 DEFAULT 对象中。

所以,我的问题:

如果某些属性完全相同,是否有办法强制将 DEFAULT 对象合并为一个对象?是这样,怎么做?我想我可以在应用启动时这样做,因为只有在将新设备添加到 iCloud 时才会发生 DEFAULT 对象的复制。

是否有完全不同的方法来处理我所遗漏的这个问题?

我在过去的 2 个月里一直在开发这个应用程序,但是我无法在同步时发送重复对象的东西,而且我不知道如何修复它,所以任何帮助都非常非常感谢。

谢谢, @cdf1982

【问题讨论】:

将 CodeData 与 ICloud 一起使用时,很难避免数据不同步。在我的公司,我们以这种方式制作了一款应用程序,我向您保证,我们永远不会再这样做了。系统的不透明性使得修复不同步数据变得非常困难。苹果对这个特定问题的支持一直非常……沉默。我对你的建议:不要将 CoreData 与 iCloud 一起使用,而且最重要的是不要相信那些告诉你它会产生奇迹的人,因为它不会。 【参考方案1】:

这是使用 iCloud 或任何同步机制的基础。如果您的应用在多个设备上创建了相同的实例,并且不能坐等查看它是否已经存在于不同的设备上,那么您将得到重复。

处理此问题的唯一方法是让重复发生,然后清理它们。使用 iCloud,您会在收到 NSPersistentStoreDidImportUbiquitousContentChangesNotification 时进行清理,这表明有新的传入数据可用。基本方案是进行提取以查找所有重复项,然后根据您的应用程序的需要(合并/删除/其他)处理它们。我在a previous answer 和in a blog post 中详细描述了这一点。

如果您的类别实体有一个存储唯一 ID 的属性,并且您确保始终为默认类别使用相同的唯一 ID 值,那么您自己会轻松很多实例。然后,您可以通过仅获取与已知唯一 ID 值匹配的对象来简化重复数据删除。

【讨论】:

您好,感谢您的回答,特别感谢您,因为自从我在您的博客文章和我面前的 Apple 的 SharedCoreData WWDC 项目中发布了这个问题以来,我已经花了很多时间:我已经有一个唯一的 ID类别实体,并按照您的帖子和 Apple 的 deDupe 示例代码,我正在尝试按照您的建议进行操作。这对我来说有点困难,因为我实际上是在将 Objective C 代码翻译成 Swift,但是在离开我的电脑之前(我现在正在打电话,抱歉打错了)我能够将重复的内容记录到控制台,所以我假设我走上了一条好路……您的帖子很宝贵 ...现在让我们看看我是否能够完成任务(我不是编程专家)。如果我愿意,我会让你和每个人都知道,从相当古老的示例项目中发布我的 Apple 的 deDupe 方法的 Swift 版本......字典有一些棘手的部分,但我很乐观。再次感谢! 早上好@TomHarrington,我想我已经成功地将 SharedCoreData 的 deDupe 方法从 Objective C 移植到了 Swift。在这里发布我的代码之前,如果可以的话,我还有一个问题:在合并更改后,我在persistentStoreDidImportUbiquitousContentChanges 中调用我的 deDeduplica 方法,但这意味着目前每次添加、更新对象时都会调用我的 deDuplicate 方法或删除,当然,这不是有效的。我看到你在博文中写道... "传入的更改通知具有插入、更新和删除对象的单独列表。如果您过去扫描过重复项,您知道不会有任何新的重复项,除非至少插入了一个新对象。当只有更新和/或删除到达时跳过重复检测。” 遗憾的是,我不得不承认我不知道如何检查传入的更改通知,所以我不知道当只有更新和删除时,知道如何跳过对 deDuplicate() 的调用。我可以请您指出正确的方向,或者您是否有一些代码作为示例?谢谢! 该通知的userInfo 字典具有名为NSInsertedObjectsKey NSUpdatedObjectsKey NSDeletedObjectsKey 的键,可用于检查已发生的更改。在这种情况下,你只关心NSInsertedObjectsKey

以上是关于合并具有相同属性但 ID 不同的 NSManagedObject,与 iCloud 同步会导致重复(Core Data,Swift 1.2)的主要内容,如果未能解决你的问题,请参考以下文章

如何将具有相同属性的对象合并到一个数组中?

合并到一个具有相同 id 或属性 SQL Group_concat 的组没有帮助:(

将具有相同 id 的行合并为一行,保留所有数据

合并具有不同列名但定义相同的多个CSV

Pandas DataFrame:合并具有相同 ID 的行

如何合并具有“相同父亲”、相同方法和相同 id=0 的两个节点(使用 XSLT)?