IOS开发之--NSPredicate
我把常用的NSPredicate使用场景整理了一下Cocoa提供了一个NSPredicate的类, 指定过滤方式筛选数据。关于性能我做了个简单的测试,结果如下:
数据源500万条数据,计算时间基于5次试验的平均值 检索数据条数 For循环时间(秒) NSPredicate时间(秒) 1(位于数据源最后一项) 0.356625 1.487944 1(位于数据源前1000项) 0.002461 1.516057 6 1.763434 2.571393 12 3.466698 3.43019 24 6.58877 5.465602 48 12.699893 9.312682 96 25.36474 17.235685 从以上测试结果看,苹果对predicate的确做了优化,这种优化随着搜索数据集合的增加逐渐显现出来,
检索1条数据时,该数据在数据源中的位置对于Nspredicate来说基本没有影响;
搜索100条数据比for循环节省8秒钟时间,
但是搜索少量数据如1-10条以内的数据时,for循环还比较占优势
下面整理一下常用方法:
NSPredicate有3种创建方式:格式化字符串/模板/代码方式,下面介绍3种方式1. 使用格式化字符串创建
语法:NSPredicate *predicate = [NSPredicate predicateWithFormat: @"name == 'Herbie'"];这里的格式化字符串方式比较丰富,可以用到多种运算符:· 比较和逻辑运算符==和=、>、>=和=>、<、<=和=<、!=和<>AND、OR、NOT逻辑运算符或者C样式的等效表达式&&、||和!示例: predicate=[NSPredicate predicateWithFormat:@"(engine.horsepower>50) AND (engine.horsepower<200)"];字符串中的运算符不区分大小写,你可以随意使用AnD、And或or,不等号既适用于数字值又适用于字符串值;另外注意表达式的写法支持KVC键路径· 数组运算符BETWEEN:
ü predicate=[NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN {50,200}"];
ü NSArray *betweens=[NSArray arrayWithObjects:
[NSNumber numberWithInt:50],
[NSNumber numberWithInt:200],nil];
predicate=[NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN %@",betweens];
ü predicateTemplate=[NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN $POWERS"];
varDict=[NSDictionary dictionaryWithObjectsAndKeys:betweens,@"POWERS",nil];
predicate=[predicateTemplate predicateWithSubstitutionVariables:varDict];
IN:
ü predicate=[NSPredicate predicateWithFormat:@"name IN {'Herbie','Snugs','Badger','Flap'}"];同理,IN也支持上面描述的between类型的构造方式· 字符串运算符BEGINSWITHENDSWITHCONTAINS· LIKE运算符
通配符: ? 匹配单个字符 * 匹配任意个字符
NSPredicate *predicate = [NSPredicate
predicateWithFormat:@"lastName like[c] \"S*\""];
· 正则表达式, MATCHES运算符
NSString *match = @"imagexyz-\\d{3}\\.png";NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF matches %@", match];
· SELF关键字格式化字符串中除了使用对象的键路径引用属性,还可以使用SELF关键字引用对象本身predicate=[NSPredicate predicateWithFormat:@"SELF.name IN {'Herbie','Snugs','Badger','Flap'}"];· [c]、[d]或[cd]修饰符为了减少名称匹配规则,可以为这些运算符添加[c]、[d]或[cd]修饰符。其中,c表示“不区分大小写”,d表示“不区分发音符 号”,[cd]表示都不区分。
例如 "name BEGINSWITH[cd] 'HERB'"· ALL,ANY关键字
对象存在关系对象时,有时还会用到ALL,ANY,指明本对象内的xx数组某一个或所有的需要满足xx条件[NSPredicate predicateWithFormat:@"ANY entryTags IN %@", selectedTags];[NSPredicate predicateWithFormat:@"ALL entryTags IN %@", selectedTags];2. 使用Predicate 模板创建
模板创建是基于字符串创建的,只不过提供了NSPredicate的模板实现方式如果使用CoreData,可以使用Xcode的设计工具为Fetchrequest添加Predicate模板,详情可参考https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreData/Articles/cdMOM.html#//apple_ref/doc/uid/TP40002328<632B09E1-5DB2-45E5-8A45-73E2B149B108.png>当然,Predicate模板不仅仅是为CoreData服务的,可以实现自己的模板:如上面的between运算符的第三种使用方式就是一种模板的实现
NSArray *betweens = [NSArray arrayWithObjects:[NSNumber numberWithInt: 50],[NSNumber numberWithInt: 200], nil];NSPredicate* predicateTemplate = [NSPredicate predicateWithFormat: @"engine.horsepower BETWEEN $POWERS"];varDict = [NSDictionary dictionaryWithObjectsAndKeys: betweens, @"POWERS", nil];predicate = [predicateTemplate predicateWithSubstitutionVariables: varDict];模板创建NSPredicate主要用于同样的过滤方式,不同的源数据情况下重用NSPredicate的情况使用。
3. 直接用代码创建
这种方式是直接使用predicate 和 expression创建NSPredicate,优点是没有使用到字符串解析等大量繁杂的字符串拼接方式,缺点是需要构造大量的expression对象,如下官网给出的实例:
NSExpression *lhs = [NSExpression expressionForKeyPath:@"revenue"]; NSExpression *greaterThanRhs = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:1000000]]; NSPredicate *greaterThanPredicate = [NSComparisonPredicate predicateWithLeftExpression:lhs rightExpression:greaterThanRhs modifier:NSDirectPredicateModifier type:NSGreaterThanOrEqualToPredicateOperatorType options:0]; NSExpression *lessThanRhs = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:100000000]]; NSPredicate *lessThanPredicate = [NSComparisonPredicate predicateWithLeftExpression:lhs rightExpression:lessThanRhs modifier:NSDirectPredicateModifier type:NSLessThanPredicateOperatorType options:0]; NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates: @[greaterThanPredicate, lessThanPredicate]];感觉这种方式用起来不太方便,也不太灵活,所以不继续深究了