NSPredicate
NSPredicate
是一个Foundation类,它指定数据被获取或者过滤的方式。它的查询语言就像SQL的WHERE
和正则表达式的交叉一样,提供了具有表现力的,自然语言界面来定义一个集合被搜寻的逻辑条件。
相比较抽象的谈论它,展示NSPredicate
的使用方法更加容易,所以我们来重新审视NSSort
中使用的示例数据集吧:
索引 | 0 | 1 | 2 | 3 |
---|---|---|---|---|
名 | Alice | Bob | Charlie | Quentin |
姓 | Smith | Jones | Smith | Alberts |
年龄 | 24 | 27 | 33 | 31 |
@interface Person : NSObject
@property NSString *first Name;
@property NSString *last Name;
@property NSNumber *age;
@end
@implementation Person
- (NSString *)description {
return [NSString string With Format:@"%@ %@", self.first Name, self.last Name];
}
@end
#pragma mark -
NSArray *first Names = @[ @"Alice", @"Bob", @"Charlie", @"Quentin" ];
NSArray *last Names = @[ @"Smith", @"Jones", @"Smith", @"Alberts" ];
NSArray *ages = @[ @24, @27, @33, @31 ];
NSMutable Array *people = [NSMutable Array array];
[first Names enumerate Objects Using Block:^(id obj, NSUInteger idx, BOOL *stop) {
Person *person = [[Person alloc] init];
person.first Name = first Names[idx];
person.last Name = last Names[idx];
person.age = ages[idx];
[people add Object:person];
}];
NSPredicate *bob Predicate = [NSPredicate predicate With Format:@"first Name = 'Bob'"];
NSPredicate *smith Predicate = [NSPredicate predicate With Format:@"last Name = %@", @"Smith"];
NSPredicate *thirties Predicate = [NSPredicate predicate With Format:@"age >= 30"];
// ["Bob Jones"]
NSLog(@"Bobs: %@", [people filtered Array Using Predicate:bob Predicate]);
// ["Alice Smith", "Charlie Smith"]
NSLog(@"Smiths: %@", [people filtered Array Using Predicate:smith Predicate]);
// ["Charlie Smith", "Quentin Alberts"]
NSLog(@"30's: %@", [people filtered Array Using Predicate:thirties Predicate]);
NSPredicate
集合中使用Foundation提供使用谓词(predicate)来过滤NSArray
/NSMutable
&NSSet
/NSMutable
的方法。
不可变的集合,NSArray
&NSSet
,有可以通过评估接收到的predicate来返回一个不可变集合的方法filtered
和filtered
。
可变集合,NSMutable
&NSMutable
,可以使用方法filter
,它可以通过运行接收到的谓词来移除评估结果为FALSE
的对象。
NSDictionary
可以用谓词来过滤它的键和值(两者都为NSArray
对象)。NSOrdered
可以由过滤的NSArray
或NSSet
生成一个新的有序的集,或者NSMutable
可以简单的remove
,来传递通过_否定_predicate过滤的对象。
NSPredicate
Core Data中使用NSFetch
有一个predicate
属性,它可以指定管理对象应该被获取的逻辑条件。谓词的使用规则在这里同样适用,唯一的区别在于,在管理对象环境中,谓词由持久化存储助理(persistent store coordinator)评估,而不像集合那样在内存中被过滤。
谓词语法
替换
%@
是对值为字符串,数字或者日期的对象的替换值。%K
是key path的替换值。
NSPredicate *age Is33Predicate = [NSPredicate predicate With Format:@"%K = %@", @"age", @33];
// ["Charlie Smith"]
NSLog(@"Age 33: %@", [people filtered Array Using Predicate:age Is33Predicate]);
$VARIABLE_NAME
是可以被NSPredicate -predicate
替换的值。With Substitution Variables:
NSPredicate *names Beginning With Letter Predicate = [NSPredicate predicate With Format:@"(first Name BEGINSWITH[cd] $letter) OR (last Name BEGINSWITH[cd] $letter)"];
// ["Alice Smith", "Quentin Alberts"]
NSLog(@"'A' Names: %@", [people filtered Array Using Predicate:[names Beginning With Letter Predicate predicate With Substitution Variables:@{@"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个或者多个字符。MATCHES
:左边的表达式根据ICU v3(更多内容请查看ICU User Guide for Regular Expressions)的regex风格比较,等于右边的表达式。
合计操作
关系操作
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
:结果始终为假
的谓词。
NSCompound Predicate
我们见过与
&或
被用在谓词格式字符串中以创建复合谓词。然而,我们也可以用NSCompound
来完成同样的工作。
例如,下列谓词是相等的:
[NSCompound Predicate and Predicate With Subpredicates:@[[NSPredicate predicate With Format:@"age > 25"], [NSPredicate predicate With Format:@"first Name = %@", @"Quentin"]]];
[NSPredicate predicate With Format:@"(age > 25) AND (first Name = %@)", @"Quentin"];
虽然语法字符串文字更加容易输入,但是在有的时候,你需要结合现有的谓词。在那些情况下,你可以使用NSCompound
&-or
。
NSComparison Predicate
同样的,如果你在读过上周的文章之后发现你使用了太多的NSExpression
的话,NSComparison
可以帮助你解决这个问题。
就像NSCompound
一样,NSComparison
从子部件构建了一个NSPredicate
--在这种情况下,左侧和右侧都是NSExpression
。
分析它的类的构造函数可以让我们一窥NSPredicate
的格式字符串是如何解析的:
+ (NSPredicate *)predicate With Left Expression:(NSExpression *)lhs
right Expression:(NSExpression *)rhs
modifier:(NSComparison Predicate Modifier)modifier
type:(NSPredicate Operator Type)type
options:(NSUInteger)options
参数
lhs
:左边的表达式。rhs
:右边的表达式。modifier
:应用的修改符。(ANY
或者ALL
)type
:谓词运算符类型。options
:要应用的选项。没有选项的话则为0
。
NSComparison Predicate
类型
enum {
NSLess Than Predicate Operator Type = 0,
NSLess Than Or Equal To Predicate Operator Type,
NSGreater Than Predicate Operator Type,
NSGreater Than Or Equal To Predicate Operator Type,
NSEqual To Predicate Operator Type,
NSNot Equal To Predicate Operator Type,
NSMatches Predicate Operator Type,
NSLike Predicate Operator Type,
NSBegins With Predicate Operator Type,
NSEnds With Predicate Operator Type,
NSIn Predicate Operator Type,
NSCustom Selector Predicate Operator Type,
NSContains Predicate Operator Type,
NSBetween Predicate Operator Type
};
typedef NSUInteger NSPredicate Operator Type;
NSComparison Predicate
选项
NSCase
:不区分大小写的谓词。你通过在谓词格式字符串中加入后面带有[c]的字符串操作(比如,”NeXT” like[c] “next”)来表达这一选项。Insensitive Predicate Option NSDiacritic
:忽视发音符号的谓词。你通过在谓词格式字符串中加入后面带有[d]的字符串操作(比如,”naïve” like[d] “naive”)来表达这一选项。Insensitive Predicate Option NSNormalized
:表示待比较的字符串已经被预处理了。这一选项取代了NSCaseInsensitivePredicateOption和NSDiacriticInsensitivePredicateOption,旨在用作性能优化的选项。你可以通过在谓词格式字符串中加入后面带有[n]的字符串(比如,”WXYZlan” matches[n] “.lan”)来表达这一选项。Predicate Option NSLocale
:表明要使用Sensitive Predicate Option <
,<=
,=
,=>
,>
作为比较的字符串应该使用区域识别的方式处理。你可以通过在<
,<=
,=
,=>
,>
其中之一的操作符后加入[l](比如,”straße” >[l] “strasse”)以便在谓词格式字符串表达这一选项。
Block谓词
最后,如果你实在不愿意学习NSPredicate
的格式语法,你也可以学学NSPredicate +predicate
。
NSPredicate *short Name Predicate = [NSPredicate predicate With Block:^BOOL(id evaluated Object, NSDictionary *bindings) {
return [[evaluated Object first Name] length] <= 5;
}];
// ["Alice Smith", "Bob Jones"]
NSLog(@"Short Names: %@", [people filtered Array Using Predicate:short Name Predicate]);
…好吧,虽然使用predicate
是懒人的做法,但它也并不是一无是处。
事实上,因为block可以封装任意的计算,所以有一个查询类是无法以NSPredicate
格式字符串形式来表达的(比如对运行时被动态计算的值的评估)。而且当同一件事情可以用NSExpression
结合自定义选择器来完成时,block为完成工作提供了一个方便的接口。
重要提示:由predicate
生成的NSPredicate
不能用于由SQLite
存储库支持的Core Data数据的提取要求。
我知道我已经说过很多次了,可是NSPredicate
真的是Cocoa的优势之一。其他语言的第三方库如果能有它一半的能力就已经很幸运了--更别提标准库了。对于我们这些应用和框架开发者来说,有它作为标准组件使得我们在处理数据时有了很大的优势。
和NSExpression
一样,NSPredicate
一直在提醒我们Foundation有多么好:它不仅仅十分有用,它精致的构架和设计也是我们写代码时灵感的来源。