【发布时间】:2011-04-03 11:31:21
【问题描述】:
简而言之,我想在 iPad 应用上将任意键/值对与 Core Data 实体的对象相关联。
我当前的解决方案是与另一个代表一对的实体建立一对多关系。在我的应用程序中,我有:
Entry <--->> ExtraAttribute
其中ExtraAttribute 具有属性key 和value,而key 对ExtraAttribute 的条目是唯一的。
虽然处理这个的代码有点复杂,但还是可以接受的。真正的问题在于排序。
我需要按该属性对具有给定 ExtraAttribute 的那些条目进行排序。使用 SQL 存储,Core Data 本身显然不可能通过与给定键关联的 ExtraAttribute 的值对条目进行排序。 (令人沮丧,因为这在其他存储中是可能的,并且在 SQL 本身中微不足道。)
我能找到的唯一技术是自己对条目进行排序,然后将displayOrder 属性写回存储区,并让Core Data 按displayOrder 排序。我在Entry 上使用以下类方法来做到这一点。 (这使用了一些未显示的方法和全局函数,但希望您能了解要点。如果没有,请询问,我会澄清。)
NSInteger entryComparator(id entry1, id entry2, void *key) {
NSString *v1 = [[entry1 valueForPropertyName:key] description];
NSString *v2 = [[entry2 valueForPropertyName:key] description];
return [v1 localizedCompare:v2];
}
@implementation Entry
...
// Unified builtin property and extraAttribute accessor;
// expects human-readable name (since that's all ExtraAttributes have).
- (id)valueForPropertyName:(NSString *)name {
if([[Entry humanReadablePropertyNames] containsObject:name]) {
return [self valueForKey:
[Entry propertyKeyForHumanReadableName:name]];
} else {
NSPredicate *p = [NSPredicate predicateWithFormat:
@"key = %@", name];
return [[[self.extraAttributes filteredSetUsingPredicate:p]
anyObject] value];
}
}
+ (void)sortByPropertyName:(NSString *)name
inManagedObjectContext:(NSManagedObjectContext *)moc {
BOOL ascending = [Entry propertyIsNaturallyAscending:name];
[Entry sortWithFunction:entryComparator
context:name ascending:ascending moc:moc];
[[NSUserDefaults standardUserDefaults]
setObject:name
forKey:@"entrySortPropertyName"];
}
// Private method.
+ (void)sortWithFunction:(NSInteger (*)(id, id, void *))sortFunction
context:(void *)context
ascending:(BOOL)ascending
moc:(NSManagedObjectContext *)moc {
NSEntityDescription *entityDescription = [NSEntityDescription
entityForName:@"Entry" inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSError *error;
NSArray *allEntries = [moc executeFetchRequest:request error:&error];
[request release];
if (allEntries == nil) {
showFatalErrorAlert(error);
}
NSArray *sortedEntries = [allEntries
sortedArrayUsingFunction:sortFunction context:context];
int i, di;
if(ascending) {
i = 0; di = 1;
} else {
i = [sortedEntries count]; di = -1;
}
for(Entry *e in sortedEntries) {
e.displayOrder = [NSNumber numberWithInteger:i];
i += di;
}
saveMOC(moc);
}
@end
这有两个主要问题:
- 速度很慢,即使是小型数据集。
- 它可能会占用任意大量内存,因此在处理大型数据集时会崩溃。
我愿意接受任何比删除 Core Data 和直接使用 SQL 更容易的建议。非常感谢。
编辑感谢您的回答。希望这能澄清问题。
这是一个典型的数据集:有 n 个 Entry 对象,每个对象都有一个 distinct 组与之关联的键/值对。这里我列出了每个条目下的键/值对:
Entry 1:
Foo => Hello world
Bar => Lorem ipsum
Entry 2:
Bar => La dee da
Baz => Goodbye cruel world
在这里,我想按“Foo”、“Bar”或“Baz”中的任何一个键对条目进行排序。如果给定的条目没有键的值,它应该像一个空字符串一样排序。
SQLite 存储无法使用 -valueForUndefinedKey: 来按未知键排序;尝试这样做会导致NSInvalidArgumentException,原因keypath Foo not found in entity <NSSQLEntity Entry id=2>。
如文档中所述,只有一组固定的选择器可用于使用 SQL 存储的排序描述符。
编辑 2
假设我的实体有三个实例 E1、E2 和 E3,并且用户将自定义属性“名称”和“年份”附加到每个实例。那么我们可能有:
E1 Bob 2010
E2 Alice 2009
E3 Charles 2007
但我们希望将这些实例呈现给用户,并按这些自定义属性中的任何一个进行排序。例如,用户可能按名称排序:
E2 Alice 2009
E1 Bob 2010
E3 Charles 2007
或按日期:
E3 Charles 2007
E2 Alice 2009
E1 Bob 2010
等等。
【问题讨论】:
-
您是否为
displayOrder属性编制了索引?这可以显着提高性能。 -
您尝试过使用 NSSortDescriptor 吗? (developer.apple.com/mac/library/documentation/Cocoa/Reference/…)
-
我已将
displayOrder编入索引。需要明确的是,缓慢的、消耗记忆的部分正在诉诸一个新的属性。 Core Data 通过 displayOrder 很好地检索预先排序的条目。我努力使用NSSortDescriptor和Core Data 的内置排序功能,但无法正常工作。如果需要,我可以解释我尝试过的一切;但是,我已经有一段时间没有解决这个问题了,我不得不重新创建大部分试验。 -
看看这个问题:stackoverflow.com/questions/2013158/… 这是如何使用自定义方法进行排序。看看这个 sn-ps,他们可能会解决你的问题:developer.apple.com/iphone/library/documentation/DataManagement/…