【问题标题】:Removing near duplicates with Core Data and Ensembles (iCloud)使用 Core Data 和 Ensembles (iCloud) 删除附近的重复项
【发布时间】:2016-04-30 23:07:09
【问题描述】:

总结

我的问题是我想在我的基于 Core Data 的 iOS 项目中删除几乎重复的内容,该项目使用 Ensembles 与 iCloud 同步。

  • 与 iCloud 的同步在我的应用程序中基本运行良好。
  • 问题是当用户在其持久存储被 Ensembles(连接到 iCloud)窃取之前在多个设备上创建类似对象时。
  • 这会产生近乎重复的内容,这实际上是正确的。
  • 我删除这些重复项的方法似乎不起作用。

详细问题

用户可以在连接到 iCloud 之前在不同的设备上创建NSManagedObjects。假设他有一个名为Car 的NSManagedObject 与名为Person 的NSManagedObject 具有“To One”关系,而后者与Car 具有“To Many”关系。这看起来像这样:

好的,假设用户有两个设备,他在每个设备上创建了两个NSManagedObjects。一个名为“Audi”的Car 和一个名为“Raphael”的Person。两者都是通过关系联系在一起的。在另一台设备上,他创建了一个名为“BMW”的Car 和另一个名为“Raphael”的Person。也相互连接。现在用户在每台设备上都有两个相似的对象:两个Person 对象都名为“Raphael”。

我的问题是,用户在同步后最终会在每台设备上拥有两个名为“Raphael”的 Person 对象。

这实际上是正确的,因为当用户窃取他的持久存储时,对象会获得它们的唯一标识符(以识别 Ensembles 中的对象)。对象实际上是不同的。但这是我想要解决的问题。

我的方法

我实现了这个委托方法并删除了 reparationContext 中的重复项。

- (BOOL)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble 
    shouldSaveMergedChangesInManagedObjectContext:(NSManagedObjectContext*)savingContext
    reparationManagedObjectContext(NSManagedObjectContext *)reparationContext {

    [reparationContext performBlockAndWait:^{

        // Find duplicates
        // Change relationships and only use the inserted Person object (the one from iCloud)
        // Delete local Person object
        [reparationContext save:nil];
    }
    return YES;
}

基本上,这似乎在合并来自第一台设备的数据的第二台设备上运行良好。但不幸的是,即使在reparationContext中删除了本地人,它似乎仍然同步到iCloud。

这会导致状态中断,因为第一台设备随后还会合并来自第二台设备的更改并再次替换已在第二台设备上删除的人员。稍后进行一些同步,该人最终在汽车关系中丢失,并且应用程序引发同步错误。

重现问题的步骤

  • 第 1 步(设备 1)

    • 创建对象
    • 数据:汽车“Audi”->人“Raphael(设备 1)”
  • 第 2 步(设备 2)

    • 创建对象
    • 数据:汽车“BMW” -> 人物“Raphael(设备 2)”
  • 第 3 步(设备 1)

    • 从存储中获取数据
    • 连接到 iCloud
    • 向 iCloud 发送数据
    • 数据:汽车“Audi” -> 人“Raphael(设备 1)”
  • 第 4 步(设备 2)

    • 从存储中获取数据
    • 连接到 iCloud
    • 从 iCloud 合并数据
    • 将设备 2 中的本地人员替换为设备 1 中的插入人员
    • 从设备 2 中删除本地人员
    • 向 iCloud 发送数据
    • 数据:
      汽车“奥迪”-> 人“拉斐尔(设备 1)”
      汽车“宝马”-> 人“拉斐尔(设备 1)”
  • 第 5 步(设备 1)

    • 从 iCloud 合并数据
    • 将设备 1 中的本地人员替换为设备 2 中的插入人员(这不应该发生)
    • 从设备 1 中删除本地人员(这不应该发生)
    • 向 iCloud 发送数据
    • 预期数据:
      汽车“奥迪”-> 人“拉斐尔(设备 1)”
      汽车“宝马”-> 人“拉斐尔(设备 1)”
    • 实际数据:
      汽车“奥迪” -> 人“拉斐尔(设备 2)”
      汽车“BMW”-> 人“Raphael(设备 2)”

实际上本地人对象“Raphael (Device 2)”在第 4 步中被删除,但它似乎仍被发送到 iCloud,因为在第 5 步中它作为插入在savingContext.insertedObjects 中从shouldSaveMergedChangesInManagedObjectContext 弹出委托方法。

据我所知,Ensembles 首先从 iCloud 中提取更改,通过委托方法询问用户是否一切都如预期的那样,然后合并到持久存储中,并在合并后将增量发送到 iCloud。

我做错了吗?或者这是一个 Ensembles 错误?

【问题讨论】:

    标签: ios core-data synchronization icloud ensembles


    【解决方案1】:

    我认为您的“reparationContext”处理程序有问题,您删除本地对象并保留远程对象。另一个设备会做同样的事情,但反之亦然,然后删除错误的对象。修复方法必须是确定性的。因此,也许您可​​以按 uniqueID 或其他内容对两个 Person 进行排序,并始终删除第一个。然后所有设备都会做同样的事情,并且不应该有乒乓同步带回删除的数据。

    【讨论】:

    • 这是一个好点!如果我有一个时间戳,我总是可以取最旧的一个。但是获取唯一 ID 并不能保证该方法是确定性的,对吗?如果第三台设备加入并具有唯一的 ID,该 ID 将被排序到第一位,则它不再具有确定性。我发现真正奇怪的是修复方法上传删除的对象。也许我的实现是错误的..
    【解决方案2】:

    有 lars 提到的问题。您确实必须小心,始终确定性地做事。对唯一 ID 进行排序是一种方法。

    就个人而言,我会选择其他两种方式来处理:

    1. 在合并完成后执行重复数据删除(再次确保它是确定性的)
    2. 使用精心挑选的全局标识符为您控制重复数据删除。

    例如,您可以使用唯一 ID Raphael。唯一需要注意的是,当您在同一台机器上创建另一个 Raphael 时,它被称为 Raphael_1(或其他名称)。

    如果您的唯一 ID 很可能是唯一的(例如,名字 + 姓氏不太可能发生冲突),Ensembles 将自动合并不同设备上的人。

    【讨论】:

    • 谢谢!使用名称作为唯一 id 将是让合并自动发生的好方法。但是用户可以编辑他的名字,理论上这可能会再次导致近乎重复..但我想在现实世界中不太可能。 我还有 2 个问题: 1) 为什么 Ensembles 会上传我在 reparationContext 中删除的对象?这是一个错误还是期望的行为? 2) 为什么要在合并后而不是在委托方法中进行重复数据删除?这不是专门做这种事的地方吗?
    • 使用委托方法有一些复杂性。对于很多事情来说,这很好。问题是在合并期间更改集没有排序,因此如果您插入对象,然后将其删除,则在第一个合并中没有任何概念。在合并后执行此操作可确保随后对删除进行排序。所以,错误,不是真的,而是复杂性,当然。在 didSave... 委托方法中进行重复数据删除应该没问题,或者在完成块中。
    • 在使用名称作为唯一 ID 方面,您应该始终确保它在本地是唯一的。那么唯一的风险是他们几乎同时在两台设备上输入“John Smith”。但在这种情况下,它们很可能指的是同一个人,因此应该将它们视为同一个对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 2020-07-30
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    相关资源
    最近更新 更多