在编写Objective-C程序时,经常需要获取一个对象的集合,并通过某些已知条件计算该集合的值。这时需要保留符合某个条件的对象,删除那些不满足条件的对象。从而提取一些有意义的对象,这便是谓词的作用。
在Cocoa中提供了一个NSpredicate的类,该类的返回值是一个数组,它用于制定过滤器的条件,例如:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age > 28"]; }
上述代码创建了一个NSpredicate对象,描述的条件是:@"age > 28",即年龄大于28。通过对象描述所需条件,从而通过谓词对每个对象进行筛选。
本节将详细讲解Objective-C谓词的基本知识和具体用法。
创建谓词
可以通过NSPredicate类来创建谓词,具体格式如下所示。
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == 'Herbie'"];
如果谓词串中的文本块未被引用,则被看做是键路径,即需要用引号表明是字符串(单引号、双引号均可)。键路径可以在后台包含许多强大的功能。
计算谓词的格式如下所示。
BOOL match = [predicate evaluateWithObject:car];
它让谓词通过某个对象来计算自己的值,给出BOOL值。下面的实例创建了一个谓词,并用指定的对象计算了谓词的值。
实例文件main.m的具体实现代码如下所示。
//创建谓词,要求name以s开头 NSPredicate *pred = [NSPredicate predicateWithFormat:@"name like 's*'"]; FKUser *user1 = [[FKUser alloc]initWithName:@"sun" pass:@"123"]; //对user1对象使用谓词执行判断 BOOL result1 = [pred evaluateWithObject:user1]; NSLog(@"user1的name是否以s开头:%d",result1); //对user2对象使用谓词执行判断 FKUser *user2 = [[FKUser alloc]initWithName:@"bai" pass:@"563"]; BOOL result2 = [pred evaluateWithObject:user2]; NSLog(@"user2的name是否以s开头:%d",result2);
执行后输出:
2021-07-21 10:39:27.882763+0800 谓词[12847:2010374] user1的name是否以s开头:1 2021-07-21 10:39:27.883336+0800 谓词[12847:2010374] user2的name是否以s开头:0
用谓词过滤集合
在Objective-C程序中,filteredArrayUsingPredicate是NSArray数组的一种类别方法,能够循环过滤数组中的内容,可以将值为YES的对象累积放到结果数组中并返回。下面的实例演示了使用谓词过滤集合的过程。
实例文件main.m的具体实现代码如下所示。
NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSNumber numberWithInt:50], [NSNumber numberWithInt:42], [NSNumber numberWithInt:20], [NSNumber numberWithInt:64], [NSNumber numberWithInt:56], nil]; //创建谓词,要求该对象自身的值大于50 NSPredicate *pred1 = [NSPredicate predicateWithFormat:@"SELF > 50"]; //使用谓词执行过滤,过滤出值大于50的集合元素 [array filterUsingPredicate:pred1]; NSLog(@"值大于50的元素:%@",array); NSSet *set = [NSSet setWithObjects:[[FKUser alloc]initWithName:@"AABB" pass:@"343"], [[FKUser alloc]initWithName:@"BB" pass:@"231"], [[FKUser alloc]initWithName:@"CC" pass:@"659"], [[FKUser alloc]initWithName:@"DD" pass:@"743"], [[FKUser alloc]initWithName:@"EE" pass:@"985"], nil]; //创建谓词,要求该对象的name值中包含'BB' NSPredicate *pred2 = [NSPredicate predicateWithFormat:@"name CONTAINS 'BB'"]; //执行过滤,过滤后集合只剩下两个元素 NSSet *newSet = [set filteredSetUsingPredicate:pred2]; NSLog(@"%@", newSet); NSArray *array1 = [newSet allObjects]; for (FKUser *user in array1) { NSLog(@"%@ %@", user.name, user.pass); }
执行上述代码后会输出:
2021-07-21 11:07:14.700935+0800 谓词[13820:2032977] 值大于50的元素:( 64, 56 ) 2021-07-21 11:07:14.702254+0800 谓词[13820:2032977] {( <FKUser: 0x600000804360>, <FKUser: 0x600000804380> )} 2021-07-21 11:07:14.702437+0800 谓词[13820:2032977] AABB 343 2021-07-21 11:07:14.702537+0800 谓词[13820:2032977] BB 231
在谓词中使用格式说明符
在谓词中可以使用格式说明符,通过%d和%@可以插入数值和字符串,使用%K表示key。在谓词中还可以引入变量名,用$设置类似环境变量的值,例如:
@"name == $NAME"
这样可以再用predicateWithSubstitutionVariables
调用来构造新的谓词(键/值字典),其中键是变量名,值是要插入的内容。但是这种情况下,不能把变量当成键路径,只能用作值。
下面的实例演示了在谓词中使用格式说明符的过程。
实例文件main.m的具体实现代码如下所示。
NSSet *set = [NSSet setWithObjects:[[FKUser alloc]initWithName:@"AABB" pass:@"343"], [[FKUser alloc]initWithName:@"BB" pass:@"231"], [[FKUser alloc]initWithName:@"CC" pass:@"659"], [[FKUser alloc]initWithName:@"DD" pass:@"743"], [[FKUser alloc]initWithName:@"EE" pass:@"598"], nil]; NSString *proPath = @"name"; NSString *value = @"BB"; //创建谓词,该谓词中包含了两个占位符 //后面的两个变量用于为占位符设置参数值 NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", proPath, value]; //执行过滤,过滤后的集合只剩下两个元素 NSSet *newSet = [set filteredSetUsingPredicate:pred]; NSArray *array = [newSet allObjects]; for (FKUser *user in array) { NSLog(@"%@ %@", user.name, user.pass); } //创建谓词,该谓词表达式中使用%K占位符,该占位符使用pass代替 //要求被比较对象的pass包含$SUBSTR子串 NSPredicate *predTemplate = [NSPredicate predicateWithFormat:@"%K CONTAINS $SUBSTR", @"pass"]; //使用NSDictionary指定SUBSTR为'43' NSPredicate *pred1 = [predTemplate predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:@"43", @"SUBSTR", nil]]; //执行过滤,过滤后的集合只剩下两个元素 NSSet *newSet1 = [set filteredSetUsingPredicate:pred1]; NSArray *array1 = [newSet1 allObjects]; for (FKUser *user in array1) { NSLog(@"%@ %@", user.name, user.pass); } //使用NSDictionary将SUBSTR的值指定为'59' NSPredicate *pred2 = [predTemplate predicateWithSubstitutionVariables:[NSDictionary dictionaryWithObjectsAndKeys:@"59", @"SUBSTR", nil]]; //执行过滤,过滤后的集合只剩下两个元素 NSSet *newSet2 = [set filteredSetUsingPredicate:pred2]; NSArray *array2 = [newSet2 allObjects]; for (FKUser *user in array2) { NSLog(@"%@ %@", user.name, user.pass); }
执行后将会输出:
2021-07-21 13:50:56.613031+0800 谓词[17663:2122598] BB 231 2021-07-21 13:50:56.614223+0800 谓词[17663:2122598] AABB 343 2021-07-21 13:50:56.614813+0800 谓词[17663:2122598] AABB 343 2021-07-21 13:50:56.614957+0800 谓词[17663:2122598] DD 743 2021-07-21 13:50:56.615292+0800 谓词[17663:2122598] EE 598 2021-07-21 13:50:56.615575+0800 谓词[17663:2122598] CC 659
摘自《Objective-C 应用开发全程实录》