NSPredicate

2017-05-03  本文已影响0人  夏天的风_song

转载自NSPredicate
NSPredicate是一个Foundation类,它指定数据被获取或者过滤的方式。它的查询语言就像SQL的WHERE和正则表达式的交叉一样,提供了具有表现力的,自然语言界面来定义一个集合被搜寻的逻辑条件。

相比较抽象的谈论它,展示NSPredicate的使用方法更加容易,所以我们来重新审视NSSortDescriptor中使用的示例数据集吧:

索引 0 1 2 3
Alice Bob Charlie Quentin
Smith Jones Smith Alberts
年龄 24 27 33 31
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property NSNumber *age;
@end

@implementation Person

- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
}

@end

#pragma mark -

NSArray *firstNames = @[ @"Alice", @"Bob", @"Charlie", @"Quentin" ];
NSArray *lastNames = @[ @"Smith", @"Jones", @"Smith", @"Alberts" ];
NSArray *ages = @[ @24, @27, @33, @31 ];

NSMutableArray *people = [NSMutableArray array];
[firstNames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    Person *person = [[Person alloc] init];
    person.firstName = firstNames[idx];
    person.lastName = lastNames[idx];
    person.age = ages[idx];
    [people addObject:person];
}];

NSPredicate *bobPredicate = [NSPredicate predicateWithFormat:@"firstName = 'Bob'"];
NSPredicate *smithPredicate = [NSPredicate predicateWithFormat:@"lastName = %@", @"Smith"];
NSPredicate *thirtiesPredicate = [NSPredicate predicateWithFormat:@"age >= 30"];

// ["Bob Jones"]
NSLog(@"Bobs: %@", [people filteredArrayUsingPredicate:bobPredicate]);

// ["Alice Smith", "Charlie Smith"]
NSLog(@"Smiths: %@", [people filteredArrayUsingPredicate:smithPredicate]);

// ["Charlie Smith", "Quentin Alberts"]
NSLog(@"30's: %@", [people filteredArrayUsingPredicate:thirtiesPredicate]);

一、集合中使用NSPredicate

Foundation提供使用谓词(predicate)来过滤NSArray/NSMutableArray&NSSet/NSMutableSet的方法。

不可变的集合,NSArray&NSSet,有可以通过评估接收到的predicate来返回一个不可变集合的方法filteredArrayUsingPredicate:filteredSetUsingPredicate:

可变集合,NSMutableArray&NSMutableSet,可以使用方法filterUsingPredicate:,它可以通过运行接收到的谓词来移除评估结果为FALSE的对象。

NSDictionary可以用谓词来过滤它的键和值(两者都为NSArray对象)。NSOrderedSet可以由过滤的NSArray或NSSet生成一个新的有序的集,或者NSMutableSet可以简单的removeObjectsInArray:,来传递通过_否定_predicate过滤的对象。

二、Core Data中使用NSPredicate

NSFetchRequest有一个predicate属性,它可以指定管理对象应该被获取的逻辑条件。谓词的使用规则在这里同样适用,唯一的区别在于,在管理对象环境中,谓词由持久化存储助理(persistent store coordinator)评估,而不像集合那样在内存中被过滤。

三、谓词语法

1、替换
  • %@是对值为字符串,数字或者日期的对象的替换值。
NSPredicate *ageIs33Predicate = [NSPredicate predicateWithFormat:@"%K = %@", @"age", @33];

// ["Charlie Smith"]
NSLog(@"Age 33: %@", [people filteredArrayUsingPredicate:ageIs33Predicate]);

$VARIABLE_NAME是可以被NSPredicate -predicateWithSubstitutionVariables:替换的值。

NSPredicate *namesBeginningWithLetterPredicate = [NSPredicate predicateWithFormat:@"(firstName BEGINSWITH[cd] $letter) OR (lastName BEGINSWITH[cd] $letter)"];

// ["Alice Smith", "Quentin Alberts"]
NSLog(@"'A' Names: %@", [people filteredArrayUsingPredicate:[namesBeginningWithLetterPredicate predicateWithSubstitutionVariables:@{@"letter": @"A"}]]);
2、基本比较

-=, ==:左边的表达式和右边的表达式相等。

3、基本复合谓词
  • AND, &&:逻辑与.
4、字符串比较
  • 字符串比较在默认的情况下是区分大小写和音调的。你可以在方括号中用关键字符c和d来修改操作符以相应的指定不区分大小写和变音符号,比如firstname - BEGINSWITH[cd] $FIRST_NAME
5、合计操作
6、布尔值谓词
  • TRUEPREDICATE:结果始终为真的谓词。

四、NSCompoundPredicate

我们见过与&或被用在谓词格式字符串中以创建复合谓词。然而,我们也可以用NSCompoundPredicate来完成同样的工作。

例如,下列谓词是相等的:


[NSCompoundPredicate andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"age > 25"], [NSPredicate predicateWithFormat:@"firstName = %@", @"Quentin"]]];

[NSPredicate predicateWithFormat:@"(age > 25) AND (firstName = %@)", @"Quentin"];

虽然语法字符串文字更加容易输入,但是在有的时候,你需要结合现有的谓词。在那些情况下,你可以使用NSCompoundPredicate -andPredicateWithSubpredicates:&-orPredicateWithSubpredicates:

五、NSComparisonPredicate

就像NSCompoundPredicate一样,NSComparisonPredicate从子部件构建了一个NSPredicate--在这种情况下,左侧和右侧都是NSExpression。 分析它的类的构造函数可以让我们一窥NSPredicate的格式字符串是如何解析的:

+ (NSPredicate *)predicateWithLeftExpression:(NSExpression *)lhs
                             rightExpression:(NSExpression *)rhs
                                    modifier:(NSComparisonPredicateModifier)modifier
                                        type:(NSPredicateOperatorType)type
                                     options:(NSUInteger)options

** 参数**

  • lhs:左边的表达式。

** NSComparisonPredicate类型**

enum {
   NSLessThanPredicateOperatorType = 0,
   NSLessThanOrEqualToPredicateOperatorType,
   NSGreaterThanPredicateOperatorType,
   NSGreaterThanOrEqualToPredicateOperatorType,
   NSEqualToPredicateOperatorType,
   NSNotEqualToPredicateOperatorType,
   NSMatchesPredicateOperatorType,
   NSLikePredicateOperatorType,
   NSBeginsWithPredicateOperatorType,
   NSEndsWithPredicateOperatorType,
   NSInPredicateOperatorType,
   NSCustomSelectorPredicateOperatorType,
   NSContainsPredicateOperatorType,
   NSBetweenPredicateOperatorType
};
typedef NSUInteger NSPredicateOperatorType;

NSComparisonPredicate选项

  • NSCaseInsensitivePredicateOption:不区分大小写的谓词。你通过在谓词格式字符串中加入后面带有[c]的字符串操作(比如,"NeXT" like[c] "next")来表达这一选项。

六、block谓词

最后,如果你实在不愿意学习NSPredicate的格式语法,你也可以学学NSPredicate +predicateWithBlock:

NSPredicate *shortNamePredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
            return [[evaluatedObject firstName] length] <= 5;
        }];

// ["Alice Smith", "Bob Jones"]
NSLog(@"Short Names: %@", [people filteredArrayUsingPredicate:shortNamePredicate]);

...好吧,虽然使用predicateWithBlock:是懒人的做法,但它也并不是一无是处。

事实上,因为block可以封装任意的计算,所以有一个查询类是无法以NSPredicate格式字符串形式来表达的(比如对运行时被动态计算的值的评估)。而且当同一件事情可以用NSExpression结合自定义选择器来完成时,block为完成工作提供了一个方便的接口。

重要提示:由predicateWithBlock:生成的NSPredicate不能用于由SQLite存储库支持的Core Data数据的提取要求。

结束语

NSPredicate真的是Cocoa的优势之一。其他语言的第三方库如果能有它一半的能力就已经很幸运了--更别提标准库了。对于我们这些应用和框架开发者来说,有它作为标准组件使得我们在处理数据时有了很大的优势。

和NSExpression一样,NSPredicate一直在提醒我们Foundation有多么好:它不仅仅十分有用,它精致的构架和设计也是我们写代码时灵感的来源。

上一篇下一篇

猜你喜欢

热点阅读