核心数据关系 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
属性,您在 awakeFromInsert
的 NSManagedObject
子类中设置。
然后,当您执行获取请求时,您可以指定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 添加/删除对象