简而言之,我想将任意键/值对与 iPad 应用程序上的核心数据实体的对象相关联。
我当前的解决方案是与代表一对的另一个实体建立一对多关系。在我的应用程序中,我有:
Entry <--->> ExtraAttribute
哪里ExtraAttribute
具有属性 key
和value
,以及key
对于 ExtraAttribute 的条目来说是唯一的。
虽然处理这个问题的代码稍微复杂一些,但是可以接受。真正的问题来自于排序。
我需要按该属性对具有给定 ExtraAttribute 的那些条目进行排序。使用 SQL 存储,Core Data 本身显然不可能根据具有给定键的关联 ExtraAttribute 的值对条目进行排序。 (令人沮丧,因为这对于其他存储来说是可能的,而对于 SQL 本身来说微不足道。)
我能找到的唯一技术是自己对条目进行排序,然后写一个 displayOrder
属性返回到商店,并让核心数据按 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
这有两个主要问题:
- 即使数据集很小,速度也很慢。
- 它可能会占用任意大量的内存,因此在处理大型数据集时会崩溃。
我愿意接受任何比剥离核心数据并直接使用 SQL 更容易的建议。非常感谢。
编辑感谢您的回答。希望这能澄清这个问题。
这是一个典型的数据集:有 n 个 Entry 对象,每个对象都有一组不同 与其关联的键/值对。在这里,我列出了每个条目下的键/值对:
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
等等。
最佳答案
第一个问题是,为什么需要将排序存储在数据库中?如果您始终在键属性中进行排序,则只要需要按排序顺序访问它们,只需使用排序描述符即可。
第二个问题,你为什么要编写自己的排序例程?
这个设计看起来相当复杂。我理解任意存储键值对的需要,我在书中设计了一个类似的系统。然而,我不清楚是否需要对这些值进行排序,也不清楚是否需要像这样的自定义排序例程。
如果您能解释排序背后的需求,我可能会建议一个更好的策略。
此外,我强烈建议研究两种方法 -valueForUndefineKey:
和 -setValue: forUndefineKey:
作为更干净的解决方案问题。这将允许您编写如下代码:
[myObject valueForKey:@"anythingInTheWorld"];
[myObject setValue:someValue forKey:@"anythingInTheWorld"];
并遵循正确的键值编码规则。
更新
-valueForUndefinedKey:
设计仅用于代码中,不可用于访问存储。我对你的目标还是有点不清楚。
给定以下模型:
Entity <-->> Property
在此设计中,Property
有两个属性:
Key
Value
从这里,您可以通过 -valueForUndefinedKey:
访问 Entity
上的任何属性,因为在幕后,Entity
将出去并获取关联的该键的属性
。因此,您可以在实体
上获得动态值。
现在是排序问题。通过这种设计,您可以直接在 SQLite 上排序,因为您实际上是在 Property
实体上排序。虽然我还不清楚排序的最终目的。它有什么值(value)?它将如何使用?
更新:重新考虑设计
我最后提出的设计是错误的。经过更深入的思考,它比我建议的要简单。您的目标可以通过原始的 Entity
<-->> Property
设计来实现。然而,在 -setValue: forKey:
方法中还有一些工作要做。逻辑如下:
- 在
Entity
上调用-setValue: forKey:
的外部代码。 -setValue: forKey:
方法尝试检索Property
。- 如果
属性
存在,则值会更新。 - 如果
属性
不存在,则为每个实体
创建一个属性
,并设置默认值(假设为空字符串) )。
唯一的性能影响是引入新 key 时。除此之外,它应该可以在没有任何性能损失的情况下工作。
关于iphone - 核心数据中的任意属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3561936/