CoreData:按类别集中的索引对项目进行排序

Posted

技术标签:

【中文标题】CoreData:按类别集中的索引对项目进行排序【英文标题】:CoreData: Sort items by index in category's set 【发布时间】:2013-04-02 15:47:50 【问题描述】:

我有一个简单的目录管理器功能,其中包含类别中的项目,除了一个项目可以属于多个类别。

Item 有 'parents' 键,它是父类别的 NSSet

Category 有 'items' 键,它是其子项的 NSOrderedSet

我正在使用NSFetchedResultController 及其代表来填满我的表格

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(ANY parents == %@)", self.category];

[fetchRequest setFetchBatchSize:30];
[fetchRequest setSortDescriptors:@[????????];

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;

NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) 
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();

所以使用这段代码我可以得到 self.category 中的项目列表

在我的 UI 中,我有 Drag and Drop 功能。所以我可以在类别中移动项目。

我的问题: Category1 和 Category2 都包含对相同项目 Item1 和 Item2 的引用。我需要在 Category1 中有 Item1、Item2 的订单,在 Category2 中有 Item2、Item1 的订单。这就是为什么我将 category.items 属性设为NSOrderedSet

但我只是无法按它们在 category.items 中的索引对项目进行排序

我尝试在排序描述符中使用块 - 没用,我尝试在排序描述符中使用选择器 - 没用,我尝试对排序描述符进行子类化 - 以某种方式工作,但在我更改时不更新项目他们的索引是 category.items。 在我的 NSSortDescriptorSubclass 中像这样:

- (NSComparisonResult)compareObject:(Item*)object1 toObject:(Item*)object2

int index1 = [self.category.items indexOfObject:object1];
int index2 = [self.category.items indexOfObject:object2];

if (index1 > index2) 
    return NSOrderedDescending;
 else if (index1 < index2) 
    return NSOrderedAscending;


return NSOrderedSame;

但是如果我在 Category.items 中更改订单,我的 UI 中的项目订单将不会更新

所以请帮我按照 category.items 集中的索引对项目进行排序。也许有一种方法可以通过一些键、运算符、表达式或其他任何东西来做到这一点。谢谢。

【问题讨论】:

你还坚持使用NSFetchedResultsController?因为你已经有一个 self.category。按索引顺序访问该有序集中的项目很简单,只需在数据源方法中调用 -objectAtIndex:。 是的,我想使用它,因为我希望我的项目在我从应用程序的其他部分更改某些属性时自动更新 好的。带有比较器块的排序描述符肯定适用于一次性排序。困难的部分是让 NSFetchedResultsController 知道关系顺序的变化。我不知道有什么方法可以做到这一点。 不幸的是,您不能在 CoreData 中使用块作为排序描述符。它只会产生一个错误。 【参考方案1】:

我建议您不要使用有序对多关系。似乎它们并没有真正按预期工作,也不符合您的需求。

相反,您需要引入自己的属性来进行排序。您可以有一个实体categoryItem,它与CategoryItem 以及属性sequence 具有一对一关系。

在您的 FRC 中,您获取 categoryItem 实体并在显示时使用 categoryItem.item 访问项目的属性。当您重新排序时(例如通过拖放),您必须重新编号所有 categoryItems 并分配正确的序列号。

-- 编辑--

当某个项目的某些细节发生变化时,将categoryItem 标记为“脏”:您可以在categoryItem 中实现一个方法markDirty,如下所示:

// CategoryItem.m
-(void)markDirty 
    [self willChangeValueForKey:@"sequence"];
    [self didChangeValueForKey:@"sequence"];


// EditItemController.m
item.attribute = newAttribute; 
for (CategoryItem *i in item.categoryItems)  [i markDirty]; 

【讨论】:

这可能行得通!非常感谢!让我尝试实现它并将您的答案标记为正确。谢谢 您知道解决方案产生的问题的解决方法吗:现在如果我更改项目中的某些参数,我的表格视图不会更新(只有在更改 categoryItem 参数时才会更新) 是的,一个微妙的问题。您可以在我编辑的答案中尝试解决方法。您是否尝试过简单的[tableView reloadData];[self.frc performFetch:self.frc.fetchRequest error:nil]; 顺便说一句,我决定实施您的markDirty 解决方案,因为它只更新我的表格视图中的一行而不是全部,只要我有来自 url 的图像,肯定会更好只更新一条记录。再次感谢!【参考方案2】:

Core Data 中的一对多关系是NSSets,而不是NSOrderedSets。由于这个原因,类别中的项目的索引不会被保存。您可以将另一个实体添加到您的核心数据模型中,称为IndexValueIndexValue 将有两个属性,categoryIdcategoryName(无论哪个有意义),然后是 categoryIndex。然后Item 将与该实体具有一对多的关系(以及一对一的反向关系,因为不需要重用这些实体,它们应该是唯一的)。对于Item 所在的每个Category,将创建一个IndexValue

编辑:似乎添加了一个有序属性来支持NSOrderedSet 中的一对多关系OS X 10.7 and ios 5。这必须是您的目标和使用。关于这个特性的文档很少,但是在获取关系时应该保持顺序而不需要使用NSSortDescriptors。至于更改顺序,我不确定这是如何支持的。 Performance on large sets of data is lower 使用此方法与我概述的上述方法相比。

【讨论】:

这并不完全正确。有序关系可从 iOS5 获得。您可以通过在数据模型的关系属性中设置有序标志来将普通关系(基于 NSSet)转换为有序关系(基于 NSOrderedSet)。 是的,这是真的,他们已经添加了这个有序的复选框,所以我想使用它

以上是关于CoreData:按类别集中的索引对项目进行排序的主要内容,如果未能解决你的问题,请参考以下文章

C++ 按索引对向量进行排序

如何按值对数组进行排序(排序)? *有一个转折*

Laravel 按相关表排序

Woocommerce 按产品类别对购物车产品进行排序

首先对 CoreData NSFetchRequest 完全匹配进行排序

Joomla K2 内容按票数排序