NSFetchRequest / 谓词问题
Posted
技术标签:
【中文标题】NSFetchRequest / 谓词问题【英文标题】:NSFetchRequest / Predicate Question 【发布时间】:2010-03-31 15:32:26 【问题描述】:我有两个实体:Patient 和 Job。 Patient 与 Job 的一对多关系称为“jobs”,而 Job 与 Patient 的一对一关系称为“patient”。 Job 具有名为“dueDate”(日期)和“完成”(BOOL)的属性,而 Patient 具有名为“firstName”和“lastName”的属性(均为字符串)。
我正在尝试为我的 NSFetchedResultsController 创建一个获取请求/谓词,我们将抓取所有尚未完成的作业(即已完成 == NO)并按患者姓名对其进行分段。这是我的代码:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Job" inManagedObjectContext:moc];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"(completed == NO)"];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *patientDescriptor = [[NSSortDescriptor alloc] initWithKey:@"patient" ascending:YES];
NSSortDescriptor *dueDateDescriptor = [[NSSortDescriptor alloc] initWithKey:@"dueDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:patientDescriptor, dueDateDescriptor, nil];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc sectionNameKeyPath:@"patient" cacheName:@"Jobs"];
这是我的 titleForHeaderInSection 方法:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:section];
NSString *firstName = [[(Job *)[fetchedResultsController objectAtIndexPath:indexPath] patient] firstName];
NSString *lastName = [[(Job *)[fetchedResultsController objectAtIndexPath:indexPath] patient] lastName];
return [NSString stringWithFormat:@"%@ %@", firstName, lastName];
这似乎不起作用。我是不是走错路了?
【问题讨论】:
一般来说,如果您准确描述“似乎不起作用”的含义,将会有所帮助。您的 tableView 显示为空白?您的 tableView 加载,但结果排序不正确?你的应用崩溃了?在这种情况下,我可以猜测发生了什么,但我的观点仍然成立。 【参考方案1】:它怎么不工作?它有助于描述您所看到的结果。
至少在您提供的示例中,您没有将排序描述符添加到您的 NSFetchRequest。
您的排序描述符无效。 Patient
似乎是一个关系,因此对关系进行排序将不起作用。你会想要做如下的排序:
NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"patient.lastName" ascending:YES];
NSSortDescriptor *firstNameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"patient.firstName" ascending:YES];
NSSortDescriptor *dueDateDescriptor = [[NSSortDescriptor alloc] initWithKey:@"dueDate" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: lastNameDescriptor, firstNameDescriptor, dueDateDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[lastNameDescriptor release], lastNameDescriptor = nil;
[firstNameDescriptor release], firstNameDescriptor = nil;
[dueDateDescriptor release], dueDateDescriptor = nil;
您确实不需要在返回 id 时需要转换 [fetchedResultsController objectAtIndexPath:indexPath]
。
你从[fetchedResultsController objectAtIndexPath:indexPath]
的电话中得到了什么?在此处放置一个断点并检查该值并确保您返回的是 NSManagedObject
而不是 nil。在该方法中设置断点也将确认您正在被调用。
您的 secondKeypathName 不会像上面提到的那样工作。您可能希望将其设置为@"patient.lastName"
,以便它与我上面描述的初始排序相匹配。
您的-tableView: titleForHeaderInSection:
应该访问由NSFetchedResultsController
提供的缓存,而不是假设该部分中将有一行:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
id sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
最后,如果您希望该部分真正显示“姓氏,名字”格式,那么您需要在Patient
实体上创建一个非瞬态派生值属性,即@ 987654331@ 以便您可以基于它创建缓存。然后,只要名字或姓氏发生更改,就需要更新此派生值。
【讨论】:
很好地理解了 sortDescriptor 中的关系。不过,您的代码中有一个错字。您创建 sortDescriptors 数组的行仍然像 OP 的原始代码一样读取。 这基本上是在说我想说的话,但是以一种更清晰、更正确的无错误方式,我把所有的错误都解决了。 :)【参考方案2】:首先,您似乎没有将 sortDescriptors 附加到 fetchRequests。这可能与问题有关,也可能无关。
其次,您可以以更简单的方式完成此操作。变成这样:
sectionNameKeyPath:@"patient.name"
"name" 应该是 Patient 对象的属性或方法。实现这一点的一种简单方法是对患者使用类别方法:
- (NSString *)name
return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
实际上,您无法通过如此简单的事情来实现您的目的,请阅读 mzarra 的答案以获得正确答案。 NSFetchedResultsController 有这样的批评意见:
如果控制器生成节,则数组中的第一个排序描述符用于将对象分组为节;其键必须与 sectionNameKeyPath 相同,或者使用其键的相对排序必须与使用 sectionNameKeyPath 的相对排序匹配。
但是,您不能对方法调用的结果进行排序,您需要对象的属性。因此,您最好的选择可能是在“患者”上有一个“名称”属性,并将该属性用于排序和 sectionNameKeyPath。
【讨论】:
添加您建议的“名称”方法没有任何问题。它甚至不必是一个类别。我认为没有令人信服的理由将其设为临时属性,因为没有需要与托管对象关联的临时数据。 我发现,对于我自己来说,将 Core Data 对象的所有这些额外方法保存在与 Core Data 生成的文件不同的单独文件中是很方便的。这样,我可以随时使用 Core Data 重新生成自动生成的主类文件(例如模型更改),而不会丢失我自己的任何代码。 谢谢。向患者实体添加另一个属性“fullName”并在我更改名字和姓氏时简单地更新它似乎效果很好。 关于品类策略的好建议。【参考方案3】:除了没有将您的 sortDescriptors 分配给您的 fetchRequest 之外,我相信您的谓词有问题。由于您正在处理核心数据,因此“已完成”属性的布尔值存储在 NSNumber 的实例中。像下面这样的谓词会更好:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"completed = %@", [NSNumber numberWithBool:NO]];
【讨论】:
以上是关于NSFetchRequest / 谓词问题的主要内容,如果未能解决你的问题,请参考以下文章
NSFetchRequest 谓词不一致 iOS (NSFetchedResultsController) 与 Mac (NSArrayController)
你如何构造 NSFetchRequest setHavingPredicate: 的谓词?