转自 http://www.cocoachina.com/industry/20140321/8024.htmlhtml
NSPredicate是一个Foundation类,它指定数据被获取或者过滤的方式。它的查询语言就像SQL的WHERE和正则表达式的交叉同样,提供了具备表现力的,天然语言界面来定义一个集合被搜寻的逻辑条件。正则表达式
索引 |
1 |
2 |
3 |
4 |
名 |
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"];
-
- NSLog(@"Bobs: %@", [people filteredArrayUsingPredicate:bobPredicate]);
-
- NSLog(@"Smiths: %@", [people filteredArrayUsingPredicate:smithPredicate]);
-
- 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)评估,而不像集合那样在内存中被过滤。
谓词语法
替换
%@是对值为字符串,数字或者日期的对象的替换值。
%K是key path的替换值。
- NSPredicate *ageIs33Predicate = [NSPredicate predicateWithFormat:@"%K = %@", @"age", @33];
-
- NSLog(@"Age 33: %@", [people filteredArrayUsingPredicate:ageIs33Predicate]);
$VARIABLE_NAME是能够被NSPredicate -predicateWithSubstitutionVariables:替换的值。
- NSPredicate *namesBeginningWithLetterPredicate = [NSPredicate predicateWithFormat:@"(firstName BEGINSWITH[cd] $letter) OR (lastName BEGINSWITH[cd] $letter)"];
-
- NSLog(@"'A' Names: %@", [people filteredArrayUsingPredicate:[namesBeginningWithLetterPredicate predicateWithSubstitutionVariables:@{@"letter": @"A"}]]);
基本比较
=, ==:左边的表达式和右边的表达式相等。
>=, =>:左边的表达式大于或者等于右边的表达式。
<=, =<:左边的表达式小于等于右边的表达式。
>:左边的表达式大于右边的表达式。
<:左边的表达式小于右边的表达式。
!=, <>:左边的表达式不等于右边的表达式。
BETWEEN:左边的表达式等于右边的表达式的值或者介于它们之间。右边是一个有两个指定上限和下限的数值的数列(指定顺序的数列)。好比,1 BETWEEN { 0 , 33 },或者$INPUT BETWEEN { $LOWER, $UPPER }。
基本复合谓词
AND, &&:逻辑与.
OR, ||:逻辑或.
NOT, !:逻辑非.
字符串比较
字符串比较在默认的状况下是区分大小写和音调的。你能够在方括号中用关键字符c和d来修改操做符以相应的指定不区分大小写和变音符号,好比firstname BEGINSWITH[cd] $FIRST_NAME。
BEGINSWITH:左边的表达式以右边的表达式做为开始。
CONTAINS:左边的表达式包含右边的表达式。
ENDSWITH:左边的表达式以右边的表达式做为结束。
LIKE:左边的表达式等于右边的表达式:?和*可做为通配符,其中?匹配1个字符,*匹配0个或者多个字符。
合计操做
关系操做
ANY,SOME:指定下列表达式中的任意元素。好比,ANY children.age < 18。
ALL:指定下列表达式中的全部元素。好比,ALL children.age < 18。
NONE:指定下列表达式中没有的元素。好比,NONE children.age < 18。它在逻辑上等于NOT (ANY ...)。
IN:等于SQL的IN操做,左边的表达必须出如今右边指定的集合中。好比,name IN { 'Ben', 'Melissa', 'Nick' }。
数组操做
array[index]:指定数组中特定索引处的元素。
array[FIRST]:指定数组中的第一个元素。
array[LAST]:指定数组中的最后一个元素。
array[SIZE]:指定数组的大小。
布尔值谓词
TRUEPREDICATE:结果始终为真的谓词。
FALSEPREDICATE:结果始终为假的谓词。
NSCompoundPredicate
咱们见过与&或被用在谓词格式字符串中以建立复合谓词。然而,咱们也能够用NSCompoundPredicate来完成一样的工做。
例如,下列谓词是相等的:
- [NSCompoundPredicate andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"age > 25"], [NSPredicate predicateWithFormat:@"firstName = %@", @"Quentin"]]];
-
- [NSPredicate predicateWithFormat:@"(age > 25) AND (firstName = %@)", @"Quentin"];
虽然语法字符串文字更加容易输入,可是在有的时候,你须要结合现有的谓词。在那些状况下,你可使用NSCompoundPredicate -andPredicateWithSubpredicates:&-orPredicateWithSubpredicates:。
NSComparisonPredicate
一样的,若是你在读过
上周的文章以后发现你使用了太多的NSExpression的话,NSComparisonPredicate能够帮助你解决这个问题。
就像NSCompoundPredicate同样,NSComparisonPredicate从子部件构建了一个NSPredicate-- 在这种状况下,左侧和右侧都是NSExpression。 分析它的类的构造函数可让咱们一窥NSPredicate的格式字符串是如何解析的:
- + (NSPredicate *)predicateWithLeftExpression:(NSExpression *)lhs
- rightExpression:(NSExpression *)rhs
- modifier:(NSComparisonPredicateModifier)modifier
- type:(NSPredicateOperatorType)type
- options:(NSUInteger)options
参数
lhs:左边的表达式。
rhs:右边的表达式。
modifier:应用的修改符。(ANY或者ALL)
type:谓词运算符类型。
options:要应用的选项。没有选项的话则为0。
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")来表达这一选项。
NSDiacriticInsensitivePredicateOption:忽视发音符号的谓词。你经过在谓词格式字符串中加入后面带有[d]的字符串操做(好比,"naïve" like[d] "naive")来表达这一选项。
NSNormalizedPredicateOption: 表示待比较的字符串已经被预处理了。这一选项取代了NSCaseInsensitivePredicateOption和 NSDiacriticInsensitivePredicateOption,旨在用做性能优化的选项。你能够经过在谓词格式字符串中加入后面带有 [n]的字符串(好比,"WXYZlan" matches[n] ".lan")来表达这一选项。
NSLocaleSensitivePredicateOption: 代表要使用<,<=,=,=>,> 做为比较的字符串应该使用区域识别的方式处理。你能够经过在<,<=,=,=>,>其中之一的操做符后加入[l](比 如,"straße" >[l] "strasse")以便在谓词格式字符串表达这一选项。
Block谓词
最后,若是你实在不肯意学习NSPredicate的格式语法,你也能够学学NSPredicate +predicateWithBlock:。
- NSPredicate *shortNamePredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
- return [[evaluatedObject firstName] length] <= 5;
- }];
-
- NSLog(@"Short Names: %@", [people filteredArrayUsingPredicate:shortNamePredicate]);
...好吧,虽然使用predicateWithBlock:是懒人的作法,但它也并非一无可取。
事实上,由于block能够封装任意的计算,因此有一个查询类是没法以NSPredicate格式字符串形式来表达的(好比对运行时被动态计算 的值的评估)。并且当同一件事情能够用NSExpression结合自定义选择器来完成时,block为完成工做提供了一个方便的接口。
重要提示:由predicateWithBlock:生成的NSPredicate不能用于由SQLite存储库支持的Core Data数据的提取要求。
我知道我已经说过不少次了,但是NSPredicate真的是Cocoa的优点之一。其余语言的第三方库若是能有它一半的能力就已经很幸运了--更别提标准库了。对于咱们这些应用和框架开发者来讲,有它做为标准组件使得咱们在处理数据时有了很大的优点。
和NSExpression同样,NSPredicate一直在提醒咱们Foundation有多么好:它不只仅十分有用,它精致的构架和设计也是咱们写代码时灵感的来源。