iOS中的谓词(NSPredicate)使用
首先,我们需要知道何谓谓词,让我们看看官方的解释:
The NSPredicate class is used to define logical conditions used to constrain a search either for a fetch or for in-memory filtering.
NSPredicate类是用来定义逻辑条件约束的获取或内存中的过滤搜索。
可以使用谓词来表示逻辑条件,用于描述对象持久性存储在内存中的对象过滤。其实意思就是:我是一个过滤器,不符合条件的都滚开
。
一、NSPredicate的基本语法
我们使用一门语言,无论是外语还是计算机语言,总是从语法开始的,这样我们才能正确的把握逻辑。所以我们从语法开始说起。在这部分我们仅关心其语法的使用
只要我们使用谓词(NSPredicate)
都需要为谓词定义谓词表达式
,而这个表达式必须是一个返回BOOL的值。
谓词表达式由表达式、运算符和值构成。
1.比较运算符
比较运算符如下
- =、==:判断两个表达式是否相等,在谓词中=和==是相同的意思都是判断,而没有赋值这一说
NSNumber *testNumber = @123;NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF = 123"];if ([predicate evaluateWithObject:testNumber]) {NSLog(@"testString:%@", testNumber);}
我们可以看到输出的内容为:
2016-01-07 11:12:27.281 PredicteDemo[4130:80412] testString:123
- >=,=>:判断左边表达式的值是否
大于或等于
右边表达式的值 - <=,=<:判断右边表达式的值是否
小于或等于
右边表达式的值 - >:判断左边表达式的值是否
大于
右边表达式的值 - <:判断左边表达式的值是否
小于
右边表达式的值 - !=、<>:判断两个表达式是否
不相等
- BETWEEN:BETWEEN表达式必须满足
表达式 BETWEEN {下限,上限}
的格式,要求该表达式必须大于或等于下限,并小于或等于上限NSNumber *testNumber = @123; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF BETWEEN {100, 200}"];if ([predicate evaluateWithObject:testNumber]) {NSLog(@"testString:%@", testNumber);} else {NSLog(@"不符合条件");}
输出结果为:
2016-01-07 11:20:39.921 PredicteDemo[4366:85408] testString:123
2.逻辑运算符
- AND、&&:逻辑与,要求两个表达式的值都为YES时,结果才为YES。
NSArray *testArray = @[@1, @2, @3, @4, @5, @6];NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF > 2 && SELF < 5"];NSArray *filterArray = [testArray filteredArrayUsingPredicate:predicate];NSLog(@"filterArray:%@", filterArray);
输出结果为:
2016-01-07 11:27:01.885 PredicteDemo[4531:89537] filterArray:(3,4 )
- OR、||:逻辑或,要求其中一个表达式为YES时,结果就是YES
- NOT、 !:逻辑非,对原有的表达式取反
3.字符串比较运算符
- BEGINSWITH:检查某个字符串是否以指定的字符串开头(如判断字符串是否以a开头:BEGINSWITH 'a')
- ENDSWITH:检查某个字符串是否以指定的字符串结尾
- CONTAINS:检查某个字符串是否包含指定的字符串
- LIKE:检查某个字符串是否匹配指定的字符串模板。其之后可以跟?
代表一个字符
和*代表任意多个字符
两个通配符。比如"name LIKE '*ac*'",这表示name的值中包含ac则返回YES;"name LIKE '?ac*'",表示name的第2、3个字符为ac时返回YES。 MATCHES:检查某个字符串是否匹配指定的正则表达式。虽然正则表达式的执行效率是最低的,但其功能是最强大的,也是我们
最常用
的。注:字符串比较都是区分
大小写
和重音符号
的。如:café和cafe是不一样的,Cafe和cafe也是不一样的。如果希望字符串比较运算不区分大小写和重音符号,请在这些运算符后使用[c]
,[d]
选项。其中[c]
是不区分大小写,[d]
是不区分重音符号,其写在字符串比较运算符之后,比如:name LIKE[cd] 'cafe'
,那么不论name是cafe、Cafe还是café上面的表达式都会返回YES。
4.集合运算符
- ANY、SOME:集合中任意一个元素满足条件,就返回YES。
- ALL:集合中所有元素都满足条件,才返回YES。
- NONE:集合中没有任何元素满足条件就返回YES。如:
NONE person.age < 18
,表示person集合中所有元素的age>=18时,才返回YES。 IN:等价于SQL语句中的IN运算符,只有当左边表达式或值出现在右边的集合中才会返回YES。我们通过一个例子来看一下
NSArray *filterArray = @[@"ab", @"abc"];NSArray *array = @[@"a", @"ab", @"abc", @"abcd"];NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", filterArray];NSLog(@"%@", [array filteredArrayUsingPredicate:predicate]);
代码的作用是将array中和filterArray中相同的元素去除,输出为:
2016-01-07 13:17:43.669 PredicteDemo[6701:136206] (a,abcd )
array[index]:返回array数组中index索引处的元素
- array[FIRST]:返回array数组中第一个元素
- array[LAST]:返回array数组中最后一个元素
- array[SIZE]:返回array数组中元素的个数
5.直接量
在谓词表达式中可以使用如下直接量
- FALSE、NO:代表逻辑假
- TRUE、YES:代表逻辑真
- NULL、NIL:代表空值
- SELF:代表正在被判断的对象自身
- "string"或'string':代表字符串
- 数组:和c中的写法相同,如:{'one', 'two', 'three'}。
- 数值:包括证书、小数和科学计数法表示的形式
- 十六进制数:0x开头的数字
- 八进制:0o开头的数字
- 二进制:0b开头的数字
6.保留字
下列单词都是保留字(不论大小写)
AND
、OR
、IN
、NOT
、ALL
、ANY
、SOME
、NONE
、LIKE
、CASEINSENSITIVE
、CI
、MATCHES
、CONTAINS
、BEGINSWITH
、ENDSWITH
、BETWEEN
、NULL
、NIL
、SELF
、TRUE
、YES
、FALSE
、NO
、FIRST
、LAST
、SIZE
、ANYKEY
、SUBQUERY
、CAST
、TRUEPREDICATE
、FALSEPREDICATE
注:虽然大小写都可以,但是更推荐使用大写来表示这些保留字
二、谓词的用法
1.定义谓词
一般我们使用下列方法来定义一个谓词
NSPredicate *predicate = [NSPredicate predicateWithFormat:<#(nonnull NSString *), ...#>];
下面我们通过几个简单的例子来看看它该如何使用:
首先我们需要定义一个模型,因为示例中需要用到它
ZLPersonModel.h
#import <Foundation/Foundation.h>typedef NS_ENUM(NSInteger, ZLPersonSex) {ZLPersonSexMale = 0,ZLPersonSexFamale
};@interface ZLPersonModel : NSObject
/** NSString 姓名 */
@property (nonatomic, copy) NSString *name;
/** NSUInteger 年龄 */
@property (nonatomic, assign, readonly) NSUInteger age;
/** ZLPersonSex 性别 */
@property (nonatomic, assign, readonly) ZLPersonSex sex;+ (instancetype)personWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex;@end
ZLPersonModel.m
#import "ZLPersonModel.h"@implementation ZLPersonModel- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex
{if (self = [super init]) {_name = name;_age = age;_sex = sex;}return self;
}+ (instancetype)personWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex
{return [[self alloc] initWithName:name age:age sex:sex];
}- (NSString *)description
{return [NSString stringWithFormat:@"[name = %@, age = %ld, sex = %ld]", self.name, self.age, self.sex];
}
@end
下面让我们进入正题
例一:(最简单的使用)
ZLPersonModel *sunnyzl = [ZLPersonModel personWithName:@"sunnyzl" age:29 sex:ZLPersonSexMale];ZLPersonModel *jack = [ZLPersonModel personWithName:@"jack" age:22 sex:ZLPersonSexMale];// 首先我们来看一些简单的使用// 1.判断姓名是否是以s开头的NSPredicate *pred1 = [NSPredicate predicateWithFormat:@"name LIKE 's*'"];// 输出为:sunnyzl:1, jack:0NSLog(@"sunnyzl:%d, jack:%d", [pred1 evaluateWithObject:sunnyzl], [pred1 evaluateWithObject:jack]);// 2.判断年龄是否大于25NSPredicate *pred2 = [NSPredicate predicateWithFormat:@"age > 25"];// 输出为:sunnyzl的年龄是否大于25:1, jack的年龄是否大于25:0NSLog(@"sunnyzl的年龄是否大于25:%d, jack的年龄是否大于25:%d", [pred2 evaluateWithObject:sunnyzl], [pred2 evaluateWithObject:jack]);
看到这里我们会发现evaluateWithObject:
方法返回的是一个BOOL值,如果符合条件就返回YES,不符合就返回NO。而即使是最简单的使用也有一些大用处,比如以前我们写判断手机号码
、邮编
等等,像我就喜欢用John Engelhart
大神的RegexKitLite
,然而由于年代久远需要导入libicucore.dylib
库(xcode7为libicucore.tbd
)且由于是mrc
又需要添加-fno-objc-arc
,至此我们才能使用。然而使用谓词让我们可以用同样简洁的代码实现相同的功能
例二:
(判断手机号是否正确)
- (BOOL)checkPhoneNumber:(NSString *)phoneNumber
{NSString *regex = @"^[1][3-8]\\d{9}$";NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];return [pred evaluateWithObject:phoneNumber];
}
看到这里是不是感觉好爽,感觉以前所有的正则都可以这么匹配,但是谓词匹配正则时也是有缺点的,下面通过一个例子来看一下这个致命的缺点
例三:谓词匹配正则的缺点
(本意:检测字符串中是否有特殊字符)
- (BOOL)checkSpecialCharacter:(NSString *)string
{NSString *regex = @"[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]";NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];return [pred evaluateWithObject:string];
}
我们想要的效果是字符串中有特殊字符时就返回YES,然而梦想是美好的,现实是残酷的
让我们看看这悲催的结局
NSString *testString = @"!";
NSLog(@"是否含有特殊字符:%d", [self checkSpecialCharacter:testString]);
// 当testString为一个特殊字符时,我们惊喜的发现输出为
// 是否含有特殊字符:1
看到这里我们心里猛然一喜,这tmd根本没问题嘛
让我们修改下testString的值
NSString *testString = @"!~";
NSLog(@"%d", [self checkSpecialCharacter:testString]);
// 我们会发现悲催的结局来了输出为
// 是否含有特殊字符:0
再次修改testString的值
NSString *testString = @"abc!~d";
NSLog(@"%d", [self checkSpecialCharacter:testString]);
// 我们会发现输出为
// 是否含有特殊字符:0
这总与我们的想法事与愿违,看到这里我们会发现谓词对正则并不像我们使用NSRegularExpression时匹配的那么好,究其原因是为什么呢?我们用NSRegularExpression时会发现匹配到一个结果时就会存入数组,再从匹配到的位置继续向下匹配。
然而NSPredicate
并不会做这样的自动操作,我们最终发现在NSPredicate
输入[`~!@#$^&*()=|{}':;',\[\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]
正则表达式时和写成^[`~!@#$^&*()=|{}':;',\[\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]$
的效果是一样的。
所以通过这个例子我们总结出来,只有在正则表达式为^表达式$
时才使用谓词,而不是所有情况都使用。
当然上例中我们可以用一个投机取巧的方法实现(但是仅能用于匹配是否包含特殊符号,而无法像NSRegularExpression那样对这些特殊符号进行复杂操作)
我们可以将- (BOOL)checkSpecialCharacter:(NSString *)string
更改为:
- (BOOL)checkSpecialCharacter:(NSString *)string
{NSString *regex = @".*[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?].*";NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];return [pred evaluateWithObject:string];
}
其实上述方法也只是匹配了一次,只不过我们将它的范围扩大了。
那么我们是不是因为这一点就摒弃它了呢,答案是否定的。因为虽然NSPredicate
有这么一点瑕疵,但是它总体带给我们的便利其实除了正则表达式匹配时的这个问题外是更多的。
2.使用谓词过滤集合
此部分是我们需要掌握的重点,因为从这里我们就可以看到谓词的真正的强大之处
其实谓词
本身就代表了一个逻辑条件,计算谓词之后返回的结果永远为BOOL类型的值。而谓词最常用的功能就是对集合进行过滤。当程序使用谓词对集合元素进行过滤时,程序会自动遍历其元素,并根据集合元素来计算谓词的值,当这个集合中的元素计算谓词并返回YES时,这个元素才会被保留下来。请注意程序会自动遍历其元素
,它会将自动遍历过之后返回为YES的值重新组合成一个集合返回。
其实类似于我们使用tableView
设置索引时使用的下段代码
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView
{return [self.cityGroup valueForKey:@"title"];
}
中的[self.cityGroup valueForKey:@"title"]
。它的作用是遍历所有title并将得到的值组成新的数组。
NSArray
提供了如下方法使用谓词来过滤集合
- (NSArray<ObjectType> *)filteredArrayUsingPredicate:(NSPredicate *)predicate
:使用指定的谓词
过滤NSArray集合,返回符合条件的元素组成的新集合NSMutableArray
提供了如下方法使用谓词来过滤集合
- (void)filterUsingPredicate:(NSPredicate *)predicate
:使用指定的谓词过滤NSMutableArray
,剔除
集合中不符合条件的元素NSSet
提供了如下方法使用谓词来过滤集合
- (NSSet<ObjectType> *)filteredSetUsingPredicate:(NSPredicate *)predicate NS_AVAILABLE(10_5, 3_0)
:作用同NSArray
中的方法NSMutableSet
提供了如下方法使用谓词来过滤集合
- (void)filterUsingPredicate:(NSPredicate *)predicate NS_AVAILABLE(10_5, 3_0)
:作用同NSMutableArray
中的方法。
通过上面的描述可以看出,使用谓词过滤不可变集合和可变集合的区别是:过滤不可变集合时,会返回符合条件
的集合元素组成的新集合;过滤可变集合时,没有返回值,会直接剔除不符合条件
的集合元素
下面让我们来看几个例子:
例一:
NSMutableArray *arrayM = [@[@20, @40, @50, @30, @60, @70] mutableCopy];// 过滤大于50的值NSPredicate *pred1 = [NSPredicate predicateWithFormat:@"SELF > 50"];[arrayM filterUsingPredicate:pred1];NSLog(@"arrayM:%@", arrayM);NSArray *array = @[[ZLPersonModel personWithName:@"Jack" age:20 sex:ZLPersonSexMale],[ZLPersonModel personWithName:@"Rose" age:22 sex:ZLPersonSexFamale],[ZLPersonModel personWithName:@"Jackson" age:30 sex:ZLPersonSexMale],[ZLPersonModel personWithName:@"Johnson" age:35 sex:ZLPersonSexMale]];// 要求取出包含‘son’的元素NSPredicate *pred2 = [NSPredicate predicateWithFormat:@"name CONTAINS 'son'"];NSArray *newArray = [array filteredArrayUsingPredicate:pred2];NSLog(@"%@", newArray);
输出为
2016-01-07 16:50:09.510 PredicteDemo[13660:293822] arrayM:(60,70
)
2016-01-07 16:50:09.511 PredicteDemo[13660:293822] ("[name = Jackson, age = 30, sex = 0]","[name = Johnson, age = 35, sex = 0]"
)
从这个例子我们就可以看到NSPredicate
有多么强大,如果让我们用其他的方法来实现又是一大堆if...else
。
让我们来回顾一下上面的从第二个数组中去除第一个数组中相同的元素
例二:
NSArray *filterArray = @[@"ab", @"abc"];NSArray *array = @[@"a", @"ab", @"abc", @"abcd"];NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", filterArray];NSLog(@"%@", [array filteredArrayUsingPredicate:predicate]);
输出为:
2016-01-07 13:17:43.669 PredicteDemo[6701:136206] (a,abcd
)
如果我们不用NSPredicate
的话,肯定又是各种if...else
,for
循环等等。可以看出NSPredicate
的出现为我们节省了大量的时间和精力。
3.在谓词中使用占位符参数
我们上面所有的例子中谓词
总是固定的
,然而我们在现实中处理变量时决定了谓词
应该是可变的
。下面我们来看看如果让谓词
变化起来。
首先如果我们想在谓词表达式
中使用变量,那么我们需要了解下列两种占位符
- %K:用于动态传入属性名
%@:用于动态设置属性值
其实相当于变量名与变量值
除此之外,还可以在谓词表达式中使用动态改变的属性值,就像环境变量一样NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF CONTAINS $VALUE"];
上述表达式中,$VALUE是一个可以动态变化的值,它其实最后是在字典中的一个key,所以可以根据你的需要写不同的值,但是必须有$开头,随着程序改变$VALUE这个
谓词
表达式的比较条件就可以动态改变。
下面我们通过一个例子来看看这三个重要的占位符应该如何使用
例一:NSArray *array = @[[ZLPersonModel personWithName:@"Jack" age:20 sex:ZLPersonSexMale],[ZLPersonModel personWithName:@"Rose" age:22 sex:ZLPersonSexFamale],[ZLPersonModel personWithName:@"Jackson" age:30 sex:ZLPersonSexMale],[ZLPersonModel personWithName:@"Johnson" age:35 sex:ZLPersonSexMale]];// 定义一个property来存放属性名,定义一个value来存放值NSString *property = @"name";NSString *value = @"Jack";// 该谓词的作用是如果元素中property属性含有值value时就取出放入新的数组内,这里是name包含JackNSPredicate *pred = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", property, value];NSArray *newArray = [array filteredArrayUsingPredicate:pred];NSLog(@"newArray:%@", newArray);// 创建谓词,属性名改为age,要求这个age包含$VALUE字符串NSPredicate *predTemp = [NSPredicate predicateWithFormat:@"%K > $VALUE", @"age"];// 指定$VALUE的值为 25NSPredicate *pred1 = [predTemp predicateWithSubstitutionVariables:@{@"VALUE" : @25}];NSArray *newArray1 = [array filteredArrayUsingPredicate:pred1];NSLog(@"newArray1:%@", newArray1);// 修改 $VALUE的值为32NSPredicate *pred2 = [predTemp predicateWithSubstitutionVariables:@{@"VALUE" : @32}];NSArray *newArray2 = [array filteredArrayUsingPredicate:pred2];NSLog(@"newArray2:%@", newArray2);
输出为
2016-01-07 17:28:02.062 PredicteDemo[14542:309494] newArray:("[name = Jack, age = 20, sex = 0]","[name = Jackson, age = 30, sex = 0]" ) 2016-01-07 17:28:02.063 PredicteDemo[14542:309494] newArray1:("[name = Jackson, age = 30, sex = 0]","[name = Johnson, age = 35, sex = 0]" ) 2016-01-07 17:28:02.063 PredicteDemo[14542:309494] newArray2:("[name = Johnson, age = 35, sex = 0]" )
从上例中我们主要可以看出来%K和$VALUE的含义。
那么至此NSPredicate
就到到此介绍完毕。
iOS中的谓词(NSPredicate)使用相关推荐
- iOS:转载:IOS谓词--NSPredicate
IOS谓词--NSPredicate 分类: IOS应用2013-02-19 17:24 6792人阅读 评论(1) 收藏 举报 Cocoa 提供了NSPredicate 用于指定过滤条件,谓词是指在 ...
- OC中给我们提供的一个技术:谓词(NSPredicate).note
OC中给我们提供的一个技术:谓词(NSPredicate) OC中的谓词操作是针对于数组类型的,他就好比数据库中的查询操作,数据源就是数组,这样的好处是我们不需要编写很多代码就可以去操作数组,同时也起 ...
- iOS中 UISearchController 搜索栏 UI技术分享
iOS中 UISearchController 搜索栏 UI技术分享 [objc] view plain copy <p style="margin-top: 0px; margin- ...
- iOS 中的单元测试
作者:bool周 原文链接:iOS 中的单元测试 最近团队内部为了保证代码质量,要求单元测试覆盖率 80%+.在编写单元测试过程中,等到了一些收获,为此总结一下. 概述 Unit test 是保证代 ...
- iOS中的正则表达式[转]
1. 下面一个简单的使用正则表达式的一个例子:NSRegularExpression 类 -(void)parseString{ //组装一个字符串,需要把里面的网址解析出来 NSString *ur ...
- IOS自带正则表达式NSPredicate的使用
首先举一个例子: 匹配9-15个由字母/数字组成的字符串的正则表达式: NSString * regex = @"^[A-Za-z0-9]{9,15}$"; NSP ...
- iOS中常用的四种数据持久化方法
iOS中的数据持久化方式,基本上有以下四种:属性列表.对象归档.SQLite3和Core Data 1.属性列表 涉及到的主要类:NSUserDefaults,一般 [NSUserDefaults s ...
- 正则表达式在iOS中的运用
http://my.oschina.net/u/1245365/blog/376517 http://my.oschina.net/joanfen/blog/415076 摘要 做项目时,经常要检测一 ...
- iOS中常用的正则表达式
正则表达式是什么鬼呀?(大神请绕路哦) 在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要.正则表达式就是用于描述这些规则的工具.换句话说,正则表达式就是记录文本规则的代码.很 ...
- Realm在iOS中的简单使用
一.Realm简单介绍 1.Realm简介 Realm是由美国YCombinator孵化的创业团队历时几年打造,第一个专门针对移动平台设计的数据库 Realm是一个跨平台的移动数据库引擎,目前支持iO ...
最新文章
- 科室鄙视链最底端,居然是这类人
- 名为 cursor_jinserted 的游标不存在_性能优化技巧 - 程序游标
- Elasticsearch检索分类详解
- 「知识图谱」领域近期值得读的 6 篇顶会论文
- dos进入mysql不记得密码_windos mysql 忘记密码,无密码登录,重新登录
- iOS-入门HelloWorld
- L2-023 图着色问题-PAT团体程序设计天梯赛GPLT
- 【LeetCode】75. 颜色分类,使得相同颜色的元素相邻
- 蛋白胶条质谱鉴定实验
- 自动驾驶-毫米波雷达系列基础篇-测速原理
- 数据库总结作业SQL操作语句三
- TensorFlow的Dataset的padded_batch使用
- 下载QQ空间鼠标图标方法
- 程序员的数学(二)—— 逻辑,真与假的二元世界
- 202007 软件市场分析
- 【Mockplus教程】如何修改手机外壳?
- ERROR : Error appeared during Puppet run: x.x.x.x _keystone.pp
- github使用指南(史上最全)
- 如何成为一名出色SEO从业人员
- 非金融机构不良资产市场的规模
热门文章
- 秒杀/抢购系统设计优化
- 49.字符串转int
- uva 1329(加权并查集)
- 绑定host域名 修改手机hosts域名
- 基于http live streaming, 使用vlc + mediastreamsegmenter + apache 实现iOS视频直播
- [Diary]花草本没有错
- vue父子组件间传参
- Solr进阶之Solr综合文本相似度的多因素权重排序实现
- “hello world!”团队第三次会议
- spring cloud 学习(5) - config server