使用to-many关系通过依赖属性对NSFetchRequest进行排序

时间:2011-05-28 16:58:28

标签: iphone objective-c core-data nsfetchrequest nssortdescriptor

我会很感激一些指导,因为我对核心数据问题有点不了解。我喜欢使用NSSortDescriptor创建NSFetchRequest,NSSortDescriptor使用基于多对多关系的依赖属性。

对于Apple来说,文档说你不能这样做。让我感到困惑的是,只有当我将它作为NSSortDescriptor的一部分使用时才会起作用。在请求的NSPredicate和我的ManagedObject子类中,它可以工作。

简而言之,我有一个简单的对象模型,它包含一个可以有许多Checkin对象的Venue对象。每个Checkin对象都有一个名为hereNow的属性。

@interface Venue :  NSManagedObject <MKAnnotation>  {}

@property (nonatomic, readonly, retain) NSNumber * hereNow;

@end

@interface Venue (CoreDataGeneratedAccessors)
- (void)addCheckinsObject:(NSManagedObject *)value;
- (void)removeCheckinsObject:(NSManagedObject *)value;
- (void)addCheckins:(NSSet *)value;
- (void)removeCheckins:(NSSet *)value;

@end

@implementation Venue 

- (NSNumber *)hereNow {

return [self valueForKeyPath:@"checkins.@sum.hereNow"];

}

@interface Checkin :  Update  {}

@property (nonatomic, retain) Venue * venue;
@property (nonatomic, retain) NSNumber * hereNow;

@end

@implementation Checkin 

@dynamic venue;
@dynamic hereNow;

@end

到目前为止一切顺利。这是来自TableView控制器的fetchRequest

// Create and configure a fetch request with the Venue entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Venue" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];

// Create a predicate to filter the results
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"checkins.@sum.hereNow > 0"];
[fetchRequest setPredicate:predicate];

// Create the sort descriptors array.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"checkins.@sum.hereNow" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];

// Create and initialize the fetch results controller.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest 
                                                                                            managedObjectContext:managedObjectContext 
                                                                                              sectionNameKeyPath:nil
                                                                                                       cacheName:@"Venues"];

使用该代码,我得到以下错误

  

由于未捕获的异常'NSInvalidArgumentException'而终止应用程序,原因:'包含KVC聚合的Keepath不应该有一个;没办法办理登记手续。@ sum.hereNow'

但是我对MapView控制器有一个类似的fetchRequest,它完美地工作(我只是设置NSSortDecriptor使用name字段)。如果我对TableView fetchRequest执行相同的操作,那么它也会起作用,并且当添加新的签入对象时,Venue计数似乎会正确更新。所以在谓词中只使用sortDecriptor似乎没有问题。

Apple's documentation但是,这说; -

  

您无法在多对多关系中设置依赖关系。例如,假设您有一个Order对象,该对象具有与OrderItem对象集合的多对多关系(orderItems),而OrderItem对象具有price属性。您可能希望Order对象具有totalPrice属性,该属性取决于关系中所有OrderItem对象的价格。您不能通过实现keyPathsForValuesAffectingValueForKey:并返回orderItems.price作为totalPrice的键路径来完成此操作。您必须观察orderItems集合中每个OrderItem对象的price属性,并通过自己更新totalPrice来响应其值的变化。

这看起来很清楚,但后来我不明白为什么我可以在谓词中做同样的事情?这是我的第一个核心数据应用,所以我对它有点新意。我已经阅读了几个关于KVO,KVC等的问题,我还没有真正掌握这些问题。正如你所看到的,我真正想要做的就是根据他们派生的总数“hereNow”对一组场地进行排序。也许我会以完全错误的方式解决这个问题?如果是这样的话,那将是一个正确方向的友好指导!

1 个答案:

答案 0 :(得分:3)

我能想到的一些事情:

  1. 在SortDescriptor上,您不能使用@sum来计算排序值。你必须在其他地方计算。我相信这就是为什么它说“含有KVC聚合物”。因为您使用“键”值来创建排序描述符,所以它必须是对象的实际属性。

  2. 如果Venue对象中有派生属性,则不应使其依赖于遍历关系的其他对象。您的“hereNow”属性无法遍历Checkin对象以获取要在Venue对象上返回的值。我相信派生属性只允许访问同一对象中的其他属性。

  3. 尝试一种可能的方法是将排序描述符键更改为尝试initWithKey:@“hereNow”。

    它与MapView控制器一起工作的原因(我从你的问题推断)是名称字段在同一个对象上,并且不会遍历关系以获得该值。

    我可以建议的最好的事情是在Venue对象上添加一个实际属性,每次更改Checkins时都会更新。我知道这不是你想要的,但它会起作用。

    顺便说一句,keyPathsForValuesAffectingValueForKey可以在Mac上运行,但不适用于iOS。它会告诉父对象何时需要更新。请参阅:https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVODependentKeys.html#//apple_ref/doc/uid/20002179-BAJEAIEE

    的“注册从属密钥”部分

    如果您是CoreData的新手,我会推荐Marcus Zarra写的一本好书:https://pragprog.com/titles/mzcd/core-data

    希望这有帮助。