核心数据关系 NSOrderedSet 在每个设备上返回不同的顺序

Posted

技术标签:

【中文标题】核心数据关系 NSOrderedSet 在每个设备上返回不同的顺序【英文标题】:Core Data Relationship NSOrderedSet returning different order on each device 【发布时间】:2016-04-05 07:53:42 【问题描述】:

我的数据模型中有几个一对多关系。在每种情况下,从模型中检索到的数据的顺序与插入它的顺序相匹配是很重要的。我已经在检查器中为每个关系勾选了“有序”框。我添加数据如下:

NSMutableOrderedSet *set = [destinationEntity mutableOrderedSetValueForKey:relationshipKey];
[set addObject:sourceEntity];
[CoreDataFunctions saveEntity:destinationEntity];

我随后按如下方式检索该集合:

NSOrderedSet *set = [sourceEntity valueForKey:relationshipKey];

在添加数据的设备上,这可以正常工作 - 我可以添加多个实体,并且它们始终以正确的顺序显示。问题是在通过 iCloud 同步接收数据的设备上,该集合以不同的顺序返回。请注意始终相同的顺序,但与在原始设备上输入的顺序不同。

为了解决这个问题,我添加了一个“order”属性,当将对象插入集合时,该属性由递增的整数填充。然后,我可以在从数据模型中检索该集合后按该键对集合进行排序。但是,如果 NSOrderedSet 没有按可预测的顺序返回,我不禁会觉得有问题。

我想知道这是否与我在设备从 iCloud 接收交易时在日志中看到的一些错误有关。我多次看到以下内容,大概每笔交易一次:

*** 错误:这个进程调用了一个 NSArray-takeing 方法,例如 initWithArray:,并传入了一个 NSSet 对象。目前正在解决此问题,但很快就会让您感到悲伤。

我认为这是因为数据模型中的对象是 NSSet 类型,而 Core Data 将它们传递给期望 NSArray 的对象。但是,我不知道我能做些什么,或者它是否与这个问题有关。我假设是这样,因为我怀疑 Core Data 正在将 NSOrderedSet 转换为 NSArray 并在此过程中丢失排序。不过我无法确认。

感谢您的任何建议!

【问题讨论】:

【参考方案1】:

最可靠的方法是创建一个Abstract Entity,类似于BaseEntity,数据模型中的所有其他类都从该BaseEntity 继承。 BaseEntity 将有一个 dateCreated 属性,您在 awakeFromInsertNSManagedObject 子类中设置。

然后,当您执行获取请求时,您可以指定NSSortDescriptor 使用dateCreated 进行排序,这非常快,尤其是在您使用SQLite 时。

如果您这样做,那么您可以取消选中 CoreData 中的 ordered 按钮。

编辑:这是一个托管对象子类中排序关系的示例:

给定一个实体Entity 具有CoreData 生成的关系属性:

@property (nullable, nonatomic, retain) NSSet<Entity *> *toManyRelationship;

然后您可以创建自己的属性:

@property (nullable, nonatomic, strong, readonly) NSArray<Entity *> *orderedToManyRelationship;

这是这样实现的:

@dynamic toManyRelationship;

- (NSArray<Entity *> *)orderedToManyRelationship

    NSSortDescriptor *dateSorter = [NSSortDescriptor sortDescriptorWithKey:@"dateCreated" ascending:YES];

    return [[self.toManyRelationship allObjects] sortedArrayUsingDescriptors:@[dateSorter]];

【讨论】:

感谢您的快速回复。只是几个问题。第一,这种方法是否意味着为我的实体使用 NSManagedObject 子类?我目前正在使用辅助方法和 KVC,因此我可以更好地控制值的设置和保存。例如,我有一个辅助方法来设置实体上的键/值,然后保存实体。第二,如果我在不使用获取请求的情况下获取关系集,这种方法会有所不同吗?例如,通过直接访问包含集合的属性。第三,如果实体被快速创建(即彼此相隔不到一秒),日期创建是否有效? OK 所以 1) 不一定是所有实体,但最简单的方法是继承 BaseEntity。注意:子类化可能是有益的,因为您可以获得类型检查和 IDE 自动完成功能,从而避免烦人的错误。 2) 如果您直接访问该属性,那么您必须将sort descriptor 应用于返回的对象。 (如果您是子类,您可以在类本身中执行此操作。) 3)是的,只要它们按照它们“被创建的顺序”插入到 NSManagedObjectContext 中。 谢谢@RASS,这很有帮助。我还意识到我的保存策略效率非常低(基本上每次更改属性值时都会保存上下文......)我现在已经实现了一种定时保存方法,该方法仅在检测到更改时保存,并且每 2 秒不超过一次。我将以您描述的方式实现托管对象子类,因为它似乎是解决问题的高度合乎逻辑的解决方案。再次感谢! 啊,好主意!所以我可以将排序方法嵌入到实体子类中,以按我想要的顺序取出实体。天才! ;) 我只是想再次跟进这个答案。创建一个抽象的BaseEntity 类作为我的其他实体的基础,并为其提供一个dateadded 属性,该属性在awakeFromInsert 上填充[NSDate date],这正是正确的方法。然后,我能够删除每个关系的ordered 状态,并在我的获取请求中对myentity.date 进行排序。再次感谢!

以上是关于核心数据关系 NSOrderedSet 在每个设备上返回不同的顺序的主要内容,如果未能解决你的问题,请参考以下文章

自定义 NSManagedObject 类从 NSOrderedSet 添加/删除对象

Core Data 在 Swift 中将数据添加到关系中

核心数据一对多插入与现有关系swift

核心数据:有序关系中第一个元素的谓词

使用 NSArrayController 管理有序的核心数据关系

Swift 2.0 一对多的关系——仍然是 NSOrderedSet?