解析和更改NSPredicate [英] Parsing and changing NSPredicate
问题描述
我必须将资料从先前的应用程式版本迁移到新版本。这也影响了用户保存的一些谓词( NSPredicate
实例),这意味着我必须以编程方式更改它们。
目前,我尝试解析使用 [NSPredicate predicateFormat]
获得的字符串,并手动更改一些表达式。例如 oldKeyPath ==something
到 newKeyPath ==something
。但是它感觉像一个黑客,我很好奇,如果有一个更好的方法?
我读了关于使用NSPredicate和NSExpression编程的Apple的文档。有很多方法从 NSExpressions
中组成 NSPredicate
对象。我希望找到从 NSPredicate
中获取 NSExpression
对象的相反方法。
感谢Martin RI能够在 NSPredicate
上创建一个类别,允许我对表达式进行修改。 p>
@implementation NSPredicate(ExtractComparisions)
- (NSPredicate *)predicateByChangingComparisionsWithBlock:(NSPredicate * NSCompoundPredicate *))block {
if([self isKindOfClass:[NSCompoundPredicate class]]){
NSCompoundPredicate * compPred =(NSCompoundPredicate *)self;
NSMutableArray * predicates = [NSMutableArray array];
for(NSPredicate * predicate in [compPred subpredicates]){
NSPredicate * newPredicate = [predicate predicateByChangingComparisionsWithBlock:block];
if(newPredicate!= nil)
[predicates addObject:newPredicate];
}
return [[[NSCompoundPredicate alloc] initWithType:compPred.compoundPredicateType
subpredicates:predicates] autorelease];
} if([self isKindOfClass:[NSComparisonPredicate class]]){
return block((NSComparisonPredicate *)self);
}
return self;
}
@end
如何使用它
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSPredicate * predicate = [NSPredicate predicateWithFormat:@key.path like%@ and count> 17,@Hello];
NSPredicate * newPredicate = [predicate predicateByChangingComparisionsWithBlock:
^ NSPredicate *(NSComparisonPredicate * cp){
NSExpression * left = [cp leftExpression];
NSExpression * right = [cp rightExpression];
if([[cp leftExpression] expressionType] == NSKeyPathExpressionType){
NSString * keyPath = [[cp leftExpression] keyPath];
if([keyPath isEqualToString:@key.path])
left = [NSExpression expressionForKeyPath:@key.new.path];
return [NSComparisonPredicate predicateWithLeftExpression:left
rightExpression:right
modifier:cp.comparisonPredicateModifier
type:cp.predicateOperatorType
options:cp.options];
}
return cp;
}];
NSLog(@Before:%@,predicate);
NSLog(@After:%@,newPredicate);
}
。)每个谓词都是子类的实例,如
NSCompoundPredicate
或 NSComparisonPredicate
。因此,您可以检查谓词的实际
类,将其转换为该类的对象,然后
检查其属性。如果必要,用子谓词或
表达式重复此过程。
以下是一个简单复合谓词如何可能的例子
解剖这样。这不是检查所有情况的一般解决方案,
,但可能指向正确的方向。
NSPredicate * p0 = [NSPredicate predicateWithFormat:@foo ='bar'AND count> 17
if([p0 isKindOfClass:[NSCompoundPredicate class]]){
NSCompoundPredicate * p0a =(NSCompoundPredicate *)p0;
NSCompoundPredicateType type0 = p0a.compoundPredicateType; // NSAndPredicateType
NSPredicate * p1 = p0a.subpredicates [0]; // foo ='bar'
NSPredicate * p2 = p0a.subpredicates [1]; // count> 17
if([p1 isKindOfClass:[NSComparisonPredicate class]]){
NSComparisonPredicate * p1a =(NSComparisonPredicate *)p1;
NSPredicateOperatorType type1 = p1a.predicateOperatorType; // NSEqualToPredicateOperatorType
NSExpression * e3 = p1a.leftExpression; // foo,NSKeyPathExpression
NSExpression * e4 = p1a.rightExpression; //bar,NSConstantValueExpression
}
if([p2 isKindOfClass:[NSComparisonPredicate class]]){
NSComparisonPredicate * p2a =(NSComparisonPredicate *)p2;
NSPredicateOperatorType type2 = p2a.predicateOperatorType; // NSGreaterThanPredicateOperatorType
NSExpression * e5 = p2a.leftExpression; // count,NSKeyPathExpression
NSExpression * e6 = p2a.rightExpression; // 17,NSConstantValueExpression
}
}
I have to migrate data from a previous app version to a new version. This also affects some predicates (NSPredicate
instances) that were saved by users, which means that I have to change them programmatically.
Currently I try to parse the string I get with [NSPredicate predicateFormat]
and change some expressions manually. For instance oldKeyPath == "something"
to newKeyPath == "something"
. But it feels like a hack and I'm curious if there is a better way?
I read Apple's documentations about programming with NSPredicate and NSExpression. There are plenty methods to compose NSPredicate
objects from NSExpressions
. I was hoping to find the reverse way to get NSExpression
objects from a NSPredicate
. Am I missing something?
Thank you for any hint.
Solution
Thanks to Martin R I was able to make a category on NSPredicate
that allows me to inject modifications to expressions.
@implementation NSPredicate (ExtractComparisions)
- (NSPredicate *)predicateByChangingComparisionsWithBlock:(NSPredicate *(^)(NSComparisonPredicate *))block {
if ([self isKindOfClass: [NSCompoundPredicate class]]) {
NSCompoundPredicate *compPred = (NSCompoundPredicate *)self;
NSMutableArray *predicates = [NSMutableArray array];
for (NSPredicate *predicate in [compPred subpredicates]) {
NSPredicate *newPredicate = [predicate predicateByChangingComparisionsWithBlock: block];
if (newPredicate != nil)
[predicates addObject: newPredicate];
}
return [[[NSCompoundPredicate alloc] initWithType: compPred.compoundPredicateType
subpredicates: predicates] autorelease];
} if ([self isKindOfClass: [NSComparisonPredicate class]]) {
return block((NSComparisonPredicate *)self);
}
return self;
}
@end
Here is a sample code on how to use it
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"key.path like %@ and count > 17", @"Hello"];
NSPredicate *newPredicate = [predicate predicateByChangingComparisionsWithBlock:
^NSPredicate *(NSComparisonPredicate *cp) {
NSExpression *left = [cp leftExpression];
NSExpression *right = [cp rightExpression];
if ([[cp leftExpression] expressionType] == NSKeyPathExpressionType) {
NSString *keyPath = [[cp leftExpression] keyPath];
if ([keyPath isEqualToString: @"key.path"])
left = [NSExpression expressionForKeyPath: @"key.new.path"];
return [NSComparisonPredicate predicateWithLeftExpression: left
rightExpression: right
modifier: cp.comparisonPredicateModifier
type: cp.predicateOperatorType
options:cp.options];
}
return cp;
}];
NSLog(@"Before: %@", predicate);
NSLog(@"After: %@", newPredicate);
}
(This is just an idea.) Every predicate is an instance of a subclass like
NSCompoundPredicate
or NSComparisonPredicate
. So you can check the actual
class of the predicate, cast it to an object of that class and then
inspect its properties. If necessary, repeat the process with sub-predicates or
expressions.
The following is just an example how a simple compound predicate could be "dissected" that way. It is not the general solution that checks for all cases, but may point you into the right direction.
NSPredicate *p0 = [NSPredicate predicateWithFormat:@"foo = 'bar' AND count > 17"];
if ([p0 isKindOfClass:[NSCompoundPredicate class]]) {
NSCompoundPredicate *p0a = (NSCompoundPredicate *)p0;
NSCompoundPredicateType type0 = p0a.compoundPredicateType; // NSAndPredicateType
NSPredicate *p1 = p0a.subpredicates[0]; // foo = 'bar'
NSPredicate *p2 = p0a.subpredicates[1]; // count > 17
if ([p1 isKindOfClass:[NSComparisonPredicate class]]) {
NSComparisonPredicate *p1a = (NSComparisonPredicate *)p1;
NSPredicateOperatorType type1 = p1a.predicateOperatorType; // NSEqualToPredicateOperatorType
NSExpression *e3 = p1a.leftExpression; // foo, NSKeyPathExpression
NSExpression *e4 = p1a.rightExpression; // "bar", NSConstantValueExpression
}
if ([p2 isKindOfClass:[NSComparisonPredicate class]]) {
NSComparisonPredicate *p2a = (NSComparisonPredicate *)p2;
NSPredicateOperatorType type2 = p2a.predicateOperatorType; // NSGreaterThanPredicateOperatorType
NSExpression *e5 = p2a.leftExpression; // count, NSKeyPathExpression
NSExpression *e6 = p2a.rightExpression; // 17, NSConstantValueExpression
}
}
这篇关于解析和更改NSPredicate的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!