简而言之,我想在iPad应用程序上将任意键/值对与Core Data实体的对象相关联。
我目前的解决方案是与代表一对的另一个实体建立多对多的关系。在我的申请中,我有:
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
这有两个主要问题:
我愿意接受任何比直接删除Core Data和直接使用SQL更容易的建议。非常感谢。
编辑感谢您的回答。希望这会澄清这个问题。
以下是一个典型的数据集: n 条目对象,每个对象都有一个不同组的键/值对。在这里,我列出了每个条目下的键/值对:
Entry 1:
Foo => Hello world
Bar => Lorem ipsum
Entry 2:
Bar => La dee da
Baz => Goodbye cruel world
这里我想通过任何键“Foo”,“Bar”或“Baz”对条目进行排序。如果给定条目没有键的值,则它应该像空字符串一样排序。
使用-valueForUndefinedKey,SQLite存储无法按未知密钥排序:;尝试这样做会产生NSInvalidArgumentException
,原因为keypath Foo not found in entity <NSSQLEntity Entry id=2>
。
如文档中所述,只有一组固定的选择器可以使用SQL存储来处理排序描述符。
编辑2
假设我的实体有三个实例E1,E2和E3,并且用户将自定义属性“Name”和“Year”附加到每个实例中。然后我们可能会:
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
等等。
答案 0 :(得分:3)
第一个问题是,为什么需要在数据库中存储排序?如果您总是在键属性中进行排序,只需在需要按排序顺序访问它们时使用排序描述符。
第二个问题,为什么要编写自己的排序程序?
这种设计看起来相当复杂。我理解需要对关键值对进行仲裁存储,我在书中设计了一个类似的系统。但是我不清楚是否需要对这些值进行排序,也不需要像这样的自定义排序例程。
如果你能解释排序背后的需要,我可能会建议一个更好的策略。
此外,我高度建议您调查两种方法-valueForUndefinedKey:
和-setValue: forUndefinedKey:
,以便更好地解决您的问题。这将允许您编写如下代码:
[myObject valueForKey:@"anythingInTheWorld"];
[myObject setValue:someValue forKey:@"anythingInTheWorld"];
并遵循正确的键值编码规则。
-valueForUndefinedKey:
设计仅用于代码,不适用于访问商店。我对你的目标仍然有点不清楚。
鉴于以下模型:
Entity <-->> Property
在此设计中,Property
有两个属性:
Key
Value
在此处,您可以通过Entity
访问-valueForUndefinedKey:
上的任何媒体资源,因为在Entity
下,Property
会出去并获取该密钥的相关Entity
。因此,您可以在Property
上获得动态值。
现在排序的问题。使用此设计,您可以直接在SQLite上进行排序,因为您实际上正在对Entity
实体进行排序。虽然我仍然不清楚排序的最终目标。它有什么价值?它将如何使用?
我提出的最后一个设计是错误的。在更深层次的反思中,它比我提出的更简单。您可以使用原始Property
&lt; - &gt;&gt;完成目标。 -setValue: forKey:
设计。但是,在-setValue: forKey:
方法中还有一些工作要做。逻辑如下:
Entity
上名为-setValue: forKey:
的外部代码。Property
方法尝试检索Property
。Property
存在,则更新该值。Property
不存在,则会为每个Entity
创建{{1}}并设置默认值(假设为空字符串)。唯一的性能影响是引入新密钥时。除此之外,它应该没有任何性能惩罚。