目录
正则表达式语法
值表达
修饰表达
NSPredicate 的简单使用
创建NSPredicate
我们使用 NSPredicate 的类方法 +predicateWithFormat: 创建了一个 NSPredicate 的对象,并传入了一个字符串参数,这个类方法使用该字符串在后台构建对象,来计算谓词的值,简单来说,就是以这个字符串来当筛选过滤的条件。
计算谓词
方法 -evaluateWithObject:
通知接收对象(即谓词)根据指定的对象计算自身的值。这里是以 name
作为键路径,使用 -valueForKeyPath:
方法来获取名称,然后将自身的这个名称(即 name)与 Herbie 进行比较,相同返回 YES,否则返回 NO。
以下是另外一个谓词:
这个谓词字符串的左侧是一个键路径,该键路径链接到 car 内部,查找 engine,然后再查找 engine 的 horsepower,然后将其与150进行比较,看它是否大于150。
数据过滤器
-filteredArrayUsingPredicate:
,这个方法是 NSArray 数组中的一种类别方法,它会循环遍历数组中的对象,根据谓词来计算每个对象的值,如果是 YES,那么就将这个对象添加到将被返回的新数组中
这样的话,只需要一行代码就可以获取筛选之后的结果了,不过这里输出的是汽车的全部信息,而不是上面那样输出汽车的名字,不过我们可以使用 KVC(键值编码)来提取其中的名称:
上面的 cars 是不可变数组,如果我们的数据是存放在可变数组里面,而且我们需要剔除不满足条件的对象的话,可以使用 NSMutableArray 的 -filterUsingPredicate:
方法:
因为 NSMutableArray
是 NSArray
的子类,所以也是可以用 -filteredArrayUsingPredicate:
方法来构建新的不可变数组的。使用谓词的确很便捷,但是它的运行速度并不会比自己编写全部代码要快,因为它无法避免要使用循环来遍历。
格式说明符
从方法 -predicateWithFormat:
可以看出来,传入的字符串是可以使用格式说明符的,比如:%d、%f 等。而且一般筛选条件都不是硬编码到项目里面的,都是根据用户的输入来进行筛选的。
除了可以使用 printf 说明符,还可以使用 %@ 来插入字符串,而 %@ 会被当做带引号的字符串:
NSPredicate 字符串中也可以使用 %K 来指定键路径:
现在,我们有一个含有变量的谓词,可以使用 -predicateWithSubstitutionVariables:
来构造新的专用谓词,创建一个键/值对字典,其中键是变量名(不包含美元符号$
),值是想要插入谓词的内容:
这里使用字符串 “Herbie”
作为键 “NAME”
的值,构造以下新谓词:
这个谓词跟之前的那些没什么不一样。
也可以用其他对象作为变量的值,例如 NSNumber,以下谓词用于过滤引擎的马力:
除了使用 NSNumber
和 NSString 之外
,也可以使用 [NSNull null]
来设置 nil
值,甚至可以使用数组。要注意的是,不能使用 "$变量名"
作为键路径,它只能表示值。使用谓词格式字符串时,如果想在程序中通过代码改变键路径,需要使用 %K
格式说明符。谓词机制不进行静态类型检查。
运算符
比较和逻辑运算符
此外,谓词字符串语法还支持括号表达式、AND、OR 和 NOT 逻辑运算符,以及用 C 语言样式表示具有相同功能的 “&&”
、”||”
和 “!”
符号。
我们可以筛选出马力在某个范围内的汽车:
上面是筛选出马力在50和200之间的汽车。
谓词字符串中的运算符不区分大小写,比如上面的 AND,可以随便写,比如:And、aNd等。
不等号既适用于数字值也适用于字符串值,比如按照字母表顺序查看所有名字排在 “Newton” 之前的汽车
数组运算符
上面筛选马力在50到200之间的汽车使用的谓词字符串为 “(engine.horsepower > 50) AND (engine.horsepower < 200)”,我们还可以写得更加简洁:
花括号表示数组,BETWEEN 将数组中的第一个元素看成数组的下限,第二个元素看成数组的上限。
可以使用 %@
格式说明符向 NSArray
数组中插入对象:
也可以使用变量:
数组不仅可以用来指定某个区间的端点值,还可以配合 IN 运算符来查找数组中是否含有某个特定值,跟 SQL 很像哈:
字符串运算符
BEGINSWITH
检查某个字符串是否以另一个字符串开头
ENDSWITH
检查某个字符串是否以另一个字符串结尾
CONTAINS
检查某个字符串是否在另一个字符串内部
使用关系运算符可以执行一些有用的操作,例如使用 “name BEGINSWITH ‘Bad’” 匹配 Badger,使用 “name ENDSWITH ‘vis’” 匹配 Elvis,以及使用 “name CONTAINS udg” 匹配 Judge。
如果是 "name BEGINSWITH 'HERB'"
这样的呢?它不会与 Herbie 匹配,因为这些匹配是区分大小写的,同样,"name BEGINSWITH 'Hérb'"
也不会与之匹配,因为其中的 e 是含有重音符的。为了避免这些情况,可以为运算符添加 [c]
、[d]
或 [cd]
修饰符,其中 c
表示 “不区分大小写”,d
表示 “不区分发音符号(diacritic insensitive,即忽略重音符)”,cd
表示 “即不区分大小写,也不区分发音符号”。通常情况下,都会使用这两个修饰符,除非有特殊需求需要区分大小写或者重音符号。
所以 Herbie
和 “name BEGINSWITH[cd] ‘HERB’”
相匹配。
LIKE 运算符
了解 SQL 的同学看到 LIKE 应该能猜到了,没错,有时候匹配开头或者结尾,又或者是否包含还不够,所以谓词格式字符串还提供了 LIKE 运算符,问号表示与一个字符匹配,型号表示与任意个字符匹配,也可以称为 “通配符”。
谓词字符串 “name LIKE *er*
将会与任何含有 er 的名称相匹配,等效于 CONTAINS。
谓词字符串 “name LIKE ‘???er*’”
将会与 Pager Car 相匹配,因为其中的 er 前面有3个字符,后面也有一些字符。但是它与 Badger 不匹配,因为 Badger 的 er 前面有4个字符。
另外,LIKE
也接收 [cd]
修饰符,用户忽略大小写和发音符号。
MATCHES
运算符
如果你喜欢用正则表达式,可以使用 MATCHES
运算符,赋给它一个正则表达式,谓词将会计算出它的值。
有 SELF 就足够了
有时候,我们可能需要将谓词应用于简单的值(例如纯文本的字符串),而不是那些通过键路径进行操作的复杂对象,比如我们需要从一汽车名称的数组中查询 name 时,就不能像之前那样子用了,这时候就轮到 SELF
出场了!
SELF
表示的是响应谓词计算的对象,事实上我们可以将谓词中所有的键路径表示成对应的 SELF 形式:
我们先获取所有的汽车名称,然后构造一个谓词,并计算该谓词的值:
结果和前面的示例是一样的:Herbie 和 Badger。
这里提一个问题,以下代码会输出什么呢?
答案如下:
对于取两个数组交集的运算来说,这种方式很巧妙。谓词包含了第一个数组的内容,因此和下面的形式类似:
现在,使用该谓词筛选第二个名称数组,在 name2 中如果有同时存在两个数组中的字符串,那么 SELF IN 会确定它是符合条件的,因此它就会保留在结果数组中,如果对象只存在于第二个数组中,那么它不会与谓词中的任何字符串匹配,所以该对象将被过滤掉,而只存在于第一个数组中的字符串因为要用来进行比较,所以将一直保留在原来的位置,不会出现在结果数组中。