全局标识符? - iCloud + Core Data + Ensembles - 删除对象时重复

时间:2015-01-23 07:36:55

标签: core-data duplicates icloud identifier ensembles

我正在尝试在我的核心数据应用中实现iCloud同步。我不是那么专业的编程,这真的是我学到的一个高级主题......我发现Drew McCormack的核心数据同步框架“Ensembles”。它似乎使iCloud Sync更容易。

我将它集成到我的应用程序中,只要我将新对象添加到我的Core Data模型中,同步就能很好地工作。但是当我删除一个对象时,它会创建重复项。然后从重复复制。我最终拥有相同的条目(对象),就像3-4次......

为什么?我究竟做错了什么?我做了一些研究,我的猜测是全局标识符可以解决这个问题吗?

什么是全局标识符?我猜他们有助于避免重复!?但是我该怎么设置呢?我真的不知道,做了很多研究,但却找不到答案。

感谢您的帮助!

更新 感谢帮助!我读了自述书和书,但因为我是初学者,所以一切都不清楚。

我想我现在理解在Ensembles中使用全局标识符,但我不知道我是否正确地使用它。

如果我理解正确,我必须为每个对象分配一个标识符。我可以通过将其存储在属性中来实现。这个标识符可以是任何东西,只要它是唯一的和NSString?

在我的应用中,用户可以存储不同的东西,请说出姓名,文字,标题,日期等。该应用程序基于Xcode中的Master-Detail-View模板,并使用Core Data。我的核心数据模型只有一个具有一些属性的实体,大多数是字符串和NSDate。没有关系或任何东西。如果用户点击“+”,则会创建一个新对象,并将用户输入的内容存储在属性中。

我添加全局标识符的做法是添加一个存储它的新属性。 所以当创建一个新对象时我会这样做

/// I did find that to use as identifier !?

NSString *taskUniqueStringKey = newManagedObject.objectID.URIRepresentation.absoluteString;

/// and store it in the attribute.

[newManagedObject setValue:taskUniqueStringKey forKey:@"coreDataObjectID"]; 

然后我用这个:

- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble globalIdentifiersForManagedObjects:(NSArray *)objects
{

return [objects valueForKeyPath:@"coreDataObjectID"];;

}

这似乎对我有用。但我做得对吗?这是分配全局标识符的正确位置吗?我没有awakeFromInsert!

如果这样做,我遇到了下一个问题。我的应用已经存在,用户在更新之前保存的旧条目将缺少全局标识符。我该怎么办?我认为我已经得到了什么以及什么是唯一的,我唯一能想到的是在创建对象时保存[NSDate date]的属性。

我试图使用它,但我失败了因为Ensembles只接受NSString而不是NSDate!?我可以使用这个日期属性,这是否足够独特并且可以作为gloabl标识符使用?如果是的话,请你给我一些代码示例,说明如何将它从日期转换为字符串?

与Ensembles同步非常有效。没有重复,您可以关闭iCloud并保留条目并再次打开它,它会像它应该同步而不会丢失本地存储的对象左右。合奏真的很酷!我看到一些小的奇怪的行为,有时同步需要很长时间,有时它真的很快,如果我在两个不同的设备上在短时间内编辑的东西,它有点混乱像我刚刚删除的对象重新出现。但我觉得这很正常吗?如果我在不同设备上使用应用程序之间花一些时间,一切正常。

我是否理解正确,只有一种方法可以调用同步:

- (void)syncWithCompletion:(void(^)(void))completion
{
if (self.ensemble.isMerging) return;


if (!self.ensemble.isLeeched) {
    [self.ensemble leechPersistentStoreWithCompletion:^(NSError *error) {
        if (error) NSLog(@"Error in leech: %@", error);

        if (completion) completion();
    }];
}
else {
    [self.ensemble mergeWithCompletion:^(NSError *error) {

        if (completion) completion();
    }];
}

如果需要你可以打电话给它?没有其他事情像以前做过合并而没有先行,或者像“这是实际状态 - 保存就像现在一样”这样的方法吗?

您要同步的应用中有不同的点。在应用程序启动时和终止时将是一个好点。在我的应用程序中有两点我应该同步我猜:当添加一个对象并将其保存到Core Data时以及当我保存对象的更改时。我还可以提供“立即同步”按钮。这是一个很好的方法,我总是打电话

[self syncWithCompletion:NULL];

出现了另一个问题。我可以从Ensembles中排除对象的同步吗?我的应用程序在第一次应用启动时将教程条目作为对象加载如果可能的话,我不想同步它们吗?

非常感谢你的帮助!如果我可以用德语本地化等帮助你,请告诉我! ;)

2 个答案:

答案 0 :(得分:6)

是的,这几乎可以肯定是因为没有为您的对象设置全局标识符,或者至少没有正确执行。

当您整合您的整体时,本地持久性存储将导入同步数据。如果没有全局标识符,Ensembles会为您的对象分配随机ID,因此它可以跨设备跟踪它们。

当您使用具有相同数据的第二台设备时,会出现重复项。 Ensembles无法知道数据表示与其他设备上相同的逻辑对象,因此它再次分配随机ID。实际上,它将每个设备上的对象视为完全独立,因此在同步后所有对象都会在数据集中结束。

解决方案是全局标识符。通过实现CDEPersistentStoreEnsemble委托方法,您可以为Ensembles提供全局ID,它可以用来识别不同设备上的哪些对象属于一起。

您应该为全球ID使用什么?通常,只是一个UUID,虽然对于像单件一样的对象,你只想选择一个id。

您可以在awakeFromInsert初始化它们。您可以将全局ID存储在实体的属性中。 (请注意,如果要迁移现有应用程序,则在尝试使用商店进行同步之前,如果已生成全局ID,则需要检查提取。)

更多详细信息位于README on GitHubbook at leanpub

更新

要回答您的更新问题:

是的,标识符必须是一个字符串,并且是不可变的。一旦分配,它就不应该改变。

NSManagedObjectID不是一个非常好的全局标识符,因为它在不同的设备上会有所不同。我们真的想要跨设备的全球性东西。

如果您从头开始,使用NSUUID是一种很好的方法。只需创建一个唯一的ID,并将其存储在对象中。

如果您有一个现有的应用程序,并且它已通过其他机制进行同步,则需要提供一种在每个设备上提供相同全局标识符的方法。一种方法是以某种方式混搭对象属性。通常情况下,这会给你一个非常接近独特的价值,并且对于过渡来说已经足够了。

例如,您可以快速获取,并发现您的对象还没有全局ID。您浏览对象,并将全局ID设置为由creationDate + text组成的字符串。 (你甚至可以通过使用哈希来缩短它,但它可能并不那么重要。)在初始“迁移”到全局标识符之后,你只需将UUID用于任何新创建的对象。

请注意,您不必使用awakeFromInsert。这只是一个方便的地方。只要在保存对象之前分配全局标识符就可以了。

NSDate获取字符串的最简单方法是调用description方法,但另一种方法是使用double获取timeIntervalSince1970并转向变成一个字符串。 (小心日期作为唯一标识符:通常一起创建的对象将具有相同的创建日期。)

您对如何进行同步是正确的:您只需拨打syncWithCompletion:

即可

回答有关排除对象的问题:您不能排除单个对象,主要是因为当这些对象与同步对象有关系时,它可能会变得棘手。您可以通过以下两种方式之一处理这些对象:

  1. 将它们放在一个单独的持久性存储中,并将该存储添加到同一个持久性存储协调器。
  2. 同步对象,但手动为它们提供全局ID,以便在每个设备上对对象进行相同处理。例如。您可以将全局ID设为“Sample1”,“Sample2”等。

答案 1 :(得分:3)

为了整合Drew的答案,我想以下两个步骤。

1 实施CDEPersistentStoreEnsemble委托方法(参见自述文件)

- (NSArray *)persistentStoreEnsemble:(CDEPersistentStoreEnsemble *)ensemble 
    globalIdentifiersForManagedObjects:(NSArray *)objects {
    return [objects valueForKeyPath:@"yourUniqueIdentifier"];
}

2 生成NSManagedObject子类

的唯一标识符
- (void)awakeFromInsert {
    [super awakeFromInsert];

    if (!self.yourUniqueIdentifier) {
        self.yourUniqueIdentifier = [[NSUUID UUID] UUIDString];
    }
}

awakeFromInsert中,您可以初始化特殊的默认属性值,例如标识符。

检查是必要的,例如,当您有父子环境时。否则,您将覆盖先前设置的标识符。请参阅Why is awakeFromInsert called twice?