NSPredicate谓词简析

本文最后更新于:2 年前

一、NSPredicate定义

NSPredicateFoundation框架中提供的一个类,中文翻译成谓词,官方文档中的注释如下:

1
// Predicates wrap some combination of expressions and operators and when evaluated return a BOOL.

意思为包含表达式和运算符的某种组合,并在计算时返回BOOL值,以次我们可以简单推测其主要的作用是用来比较的

官方文档释义如下:

A definition of logical conditions used to constrain a search either for a fetch or for in-memory filtering.

Predicates represent logical conditions, which you can use to filter collections of objects. Although it’s common to create predicates directly from instances of NSComparisonPredicate, NSCompoundPredicate, and NSExpression, you often create predicates from a format string which is parsed by the class methods on NSPredicate.

二、相关方法和属性

经常使用的是如下的初始化方法,通过格式化字符串来进行谓词对象的初始化

1
2
3
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat argumentArray:(nullable NSArray *)arguments;
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat arguments:(va_list)argList;

例如:

1
2
3
4
5
//检测length为5的对象
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"length = 5"];
NSArray *test = @[@"1213", @"dddda", @"adas", @"f", @"ccccc"];
NSArray *result = [test filteredArrayUsingPredicate:predicate];
NSLog(@"%@", result);//打印result值得到数组中的两个值@"dddda"和@"ccccc"

使用字符串初始化时,需要注意语法的正确,以及正确的转义字符,否则会引起崩溃。

除了字符串初始化NSPredicate对象以外,通过模板来创建谓词对象也是一种十分常用的方式,模板中只有键名,没有键值,键值需要在字典中进行提供,例如:

1
2
3
4
5
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"length = $LENGTH"];
predicate = [predicate predicateWithSubstitutionVariables:@{@"LENGTH" : @5}];
NSArray *test = @[@"1213", @"dddda", @"adas", @"f", @"ccccc"];
NSArray *result = [test filteredArrayUsingPredicate:predicate];
NSLog(@"%@", result);//值为@"dddda"和@"ccccc"数组

其他属性与方法解析如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//创建一个总是验证通过或不通过的谓词对象,如果是验证通过的,则任何检索都会成功进行返回,否则任何检索都会失败且不返回任何对象
+ (NSPredicate *)predicateWithValue:(BOOL)value;

//检索函数的Block版本,可以更加方遍的书写逻辑,类似遍历方法
+ (NSPredicate*)predicateWithBlock:(BOOL (^)(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

//格式化字符串属性
@property (readonly, copy) NSString *predicateFormat;

//检查一个Object对象是否可以通过验证
- (BOOL)evaluateWithObject:(nullable id)object; // evaluate a predicate against a single object

//用谓词模板进行对象的验证
- (BOOL)evaluateWithObject:(nullable id)object substitutionVariables:(nullable NSDictionary<NSString *, id> *)bindings API_AVAILABLE(macos(10.5), ios(3.0), watchos(2.0), tvos(9.0)); // single pass evaluation substituting variables from the bindings dictionary for any variable expressions encountered

//允许安全评估
- (void)allowEvaluation API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0)); // Force a predicate which was securely decoded to allow evaluation

不同的初始化方法可以搭配不同filter的方法,此处不再一一演示各个方法的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@interface NSArray<ObjectType> (NSPredicateSupport)
//根据对象数组计算谓词并返回已过滤的数组
- (NSArray<ObjectType> *)filteredArrayUsingPredicate:(NSPredicate *)predicate; // evaluate a predicate against an array of objects and return a filtered array
@end

@interface NSMutableArray<ObjectType> (NSPredicateSupport)
//根据对象数组计算谓词并直接过滤可变数组
- (void)filterUsingPredicate:(NSPredicate *)predicate; // evaluate a predicate against an array of objects and filter the mutable array directly
@end


@interface NSSet<ObjectType> (NSPredicateSupport)
//根据一组对象评估谓词并返回一个过滤集
- (NSSet<ObjectType> *)filteredSetUsingPredicate:(NSPredicate *)predicate API_AVAILABLE(macos(10.5), ios(3.0), watchos(2.0), tvos(9.0)); // evaluate a predicate against a set of objects and return a filtered set
@end

@interface NSMutableSet<ObjectType> (NSPredicateSupport)
//根据一组对象评估谓词并直接过滤可变集
- (void)filterUsingPredicate:(NSPredicate *)predicate API_AVAILABLE(macos(10.5), ios(3.0), watchos(2.0), tvos(9.0)); // evaluate a predicate against a set of objects and filter the mutable set directly
@end

@interface NSOrderedSet<ObjectType> (NSPredicateSupport)
//根据有序对象集评估谓词并返回已过滤的有序集
- (NSOrderedSet<ObjectType> *)filteredOrderedSetUsingPredicate:(NSPredicate *)p API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)); // evaluate a predicate against an ordered set of objects and return a filtered ordered set

@end

@interface NSMutableOrderedSet<ObjectType> (NSPredicateSupport)
//根据有序对象集评估谓词,并直接过滤可变有序集
- (void)filterUsingPredicate:(NSPredicate *)p API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0)); // evaluate a predicate against an ordered set of objects and filter the mutable ordered set directly

@end

三、拓展

前面官方文档中提到了几个NSPredicate的子类

1、NSComparisonPredicate比较谓词

同样是获取数组中的指定元素,相同的例子可以使用NSComparisonPredicate写成如下形式:

1
2
3
4
5
6
7
8
//创建左侧表达式对象 对应为键
NSExpression *left = [NSExpression expressionForKeyPath:@"length"];
//创建右侧表达式对象 对应为值
NSExpression *right = [NSExpression expressionForConstantValue:@5];
//创建比较谓词对象 这里设置为严格等于
NSComparisonPredicate *cPredicate = [NSComparisonPredicate predicateWithLeftExpression:left rightExpression:right modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:NSCaseInsensitivePredicateOption];
NSArray *test = @[@"1213", @"dddda", @"adas", @"f", @"ccccc"];
NSArray *result = [test filteredArrayUsingPredicate:cPredicate];

其中包含的几个枚举如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Describes how the operator is modified: can be direct, ALL, or ANY
typedef NS_ENUM(NSUInteger, NSComparisonPredicateModifier) {
NSDirectPredicateModifier = 0, // Do a direct comparison//直接进行比较操作
NSAllPredicateModifier, // ALL toMany.x = y//只有当内部所有元素都通过验证时,才算通过
NSAnyPredicateModifier // ANY toMany.x = y//当内部有一个元素满足时,就算通过验证
//后两个一般用于数组
};

// Type basic set of operators defined. Most are obvious; NSCustomSelectorPredicateOperatorType allows a developer to create an operator which uses the custom selector specified in the constructor to do the evaluation.
typedef NS_ENUM(NSUInteger, NSPredicateOperatorType) {
NSLessThanPredicateOperatorType = 0, // compare: returns NSOrderedAscending// 小于
NSLessThanOrEqualToPredicateOperatorType, // compare: returns NSOrderedAscending || NSOrderedSame// 小于等于
NSGreaterThanPredicateOperatorType, // compare: returns NSOrderedDescending// 大于
NSGreaterThanOrEqualToPredicateOperatorType, // compare: returns NSOrderedDescending || NSOrderedSame// 大于等于
NSEqualToPredicateOperatorType, // isEqual: returns true// 等于
NSNotEqualToPredicateOperatorType, // isEqual: returns false//不等于
NSMatchesPredicateOperatorType,// 正则比配
NSLikePredicateOperatorType,// Like匹配
NSBeginsWithPredicateOperatorType,// 左边的表达式以右边的表达式作为开头
NSEndsWithPredicateOperatorType,// 左边的表达式以右边的表达式作为结尾
NSInPredicateOperatorType, // rhs contains lhs returns true// 左边的表达式出现在右边的集合中
NSCustomSelectorPredicateOperatorType,// 使用自定义的函数来进行验证
NSContainsPredicateOperatorType API_AVAILABLE(macos(10.5), ios(3.0), watchos(2.0), tvos(9.0)) = 99, // lhs contains rhs returns true// 左边的集合包括右边的元素
NSBetweenPredicateOperatorType API_AVAILABLE(macos(10.5), ios(3.0), watchos(2.0), tvos(9.0))// 左边表达式的值在右边的范围中
};

// Flags(s) that can be passed to the factory to indicate that a operator operating on strings should do so in a case insensitive fashion.
typedef NS_OPTIONS(NSUInteger, NSComparisonPredicateOptions) {
NSCaseInsensitivePredicateOption = 0x01,// 不区分大小写
NSDiacriticInsensitivePredicateOption = 0x02,// 不区分读音符号
NSNormalizedPredicateOption API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 0x04, /* Indicate that the strings to be compared have been preprocessed; this supersedes other options and is intended as a performance optimization option *///比较前进行预处理 代替上面两个选项
};

2、NSCompoundPredicate复合谓词

1
2
3
4
5
6
// 创建与运算
+ (NSCompoundPredicate *)andPredicateWithSubpredicates:(NSArray<NSPredicate *> *)subpredicates NS_SWIFT_NAME(init(andPredicateWithSubpredicates:));
// 创建或运算
+ (NSCompoundPredicate *)orPredicateWithSubpredicates:(NSArray<NSPredicate *> *)subpredicates NS_SWIFT_NAME(init(orPredicateWithSubpredicates:));
// 创建非运算
+ (NSCompoundPredicate *)notPredicateWithSubpredicate:(NSPredicate *)predicate NS_SWIFT_NAME(init(notPredicateWithSubpredicate:));

四、iOS正则使用

NSPredicate谓词的用法比较冷门,但是其中包含了一个非常重要的正则表达式的用法,通过SELF MATCHES的格式化字符串,引入通用的正则表达式的判断逻辑,例如:

1
2
3
4
5
// 判断字符串是否符合规则
NSString *reg = @"^[0-9]+$";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", reg];
BOOL validate1 = [predicate evaluateWithObject:@"qw"];//NO
BOOL validate2 = [predicate evaluateWithObject:@"12"];//YES

需要注意的点如下:

  • iOS中正则表达式有些字符需要有额外的转义,否则会崩溃
  • NSPredicate中只提供了匹配方法,没有其他语音的提取方法,一些提取的操作需要使用iOS其他方法配合使用

虽然谓词比较冷门,但是关于谓词的使用,还是有很多灵活的用法等待我们去研究。