iOS谓词
在编写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 应用开发全程实录》