NSPredicate

一、简介

  NSPredicate指定数据被获取或者过滤的方式,是一个Foundation类。

  Cocoa框架中的NSPredicate用于指定过滤器的条件(即查询),它的原理和用法都像SQL的WHERE和正则表达式一样,作用相当于数据库的过滤器。

  最常用到的函数:+  predicateWithFormat: 创建谓词串

+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;

NSPredicate *predicate  = [NSPredicate predicateWithFormat:@"name == 'Herbie'"];

//注意:如果谓词串中的文本块未被引用,则被看做是键路径,即需要用引号表明是字符串,单引号,双引号均可.键路径可以在后台包含许多强大的功能

  

二、谓词语法

范例数据源:

索引0123
Alice Bob Charlie Quentin
Smith Jones Smith Alberts
年龄 24 27 33 31

背景:

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property NSNumber *age;
@end

NSArray *firstNames = @[ @"Alice", @"Bob", @"Charlie", @"Quentin" ];
NSArray *lastNames = @[ @"Smith", @"Jones", @"Smith", @"Alberts" ];
NSArray *ages = @[ @24, @27, @33, @31 ];

NSMutableArray *people = [NSMutableArray array];
[firstNames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    Person *person = [[Person alloc] init];
    person.firstName = firstNames[idx];
    person.lastName = lastNames[idx];
    person.age = ages[idx];
    [people addObject:person];
}];

NSPredicate *bobPredicate = [NSPredicate predicateWithFormat:@"firstName = 'Bob'"];
NSPredicate *smithPredicate = [NSPredicate predicateWithFormat:@"lastName = %@", @"Smith"];
NSPredicate *thirtiesPredicate = [NSPredicate predicateWithFormat:@"age >= 30"];

// ["Bob Jones"]
NSLog(@"Bobs: %@", [people filteredArrayUsingPredicate:bobPredicate]);

// ["Alice Smith", "Charlie Smith"]
NSLog(@"Smiths: %@", [people filteredArrayUsingPredicate:smithPredicate]);

// ["Charlie Smith", "Quentin Alberts"]
NSLog(@"30's: %@", [people filteredArrayUsingPredicate:thirtiesPredicate]);

 

 

1.替换:

  为了简便,多数不使用替换,而是直接输入 key path 、对象 和 $VARIABLE_NAME 。
  • %@是对值为字符串,数字或者日期的对象的替换值。
  • %K是key path的替换值。
  • $VARIABLE_NAME 是可以被 NSPredicate -predicateWithSubstitutionVariables: 替换的值。
NSPredicate *ageIs33Predicate = [NSPredicate predicateWithFormat:@"%K = %@", @"age", @33];
//为了简便,也写作:@"age = 33"
NSLog(@"Age 33: %@", [people filteredArrayUsingPredicate:ageIs33Predicate]);
//运行结果: ["Charlie Smith"]

NSPredicate *namesBeginningWithLetterPredicate = [NSPredicate predicateWithFormat:@"(firstName BEGINSWITH[cd] $letter) OR (lastName BEGINSWITH[cd] $letter)"];
//为了简便,也写作:@"(firstName BEGINSWITH[cd] 'A') OR (lastName BEGINSWITH[cd] 'A')"
NSLog(@"'A' Names: %@", [people filteredArrayUsingPredicate:[namesBeginningWithLetterPredicate predicateWithSubstitutionVariables:@{@"letter": @"A"}]]);
// 运行结果:["Alice Smith", "Quentin Alberts"]

 

2.基本比较:比较运算符

  • ===:左右相等。
  • >==>:大于或者等于。
  • <==<:小于等于。
  • >
  • <
  • !=<>:不等于。
  • BETWEEN:左边的表达式等于右边的表达式的值或者介于它们之间。

  右边是一个有两个指定上限和下限的数值的数列(指定顺序的数列)。

  比如,1 BETWEEN { 0 , 33 },或者$INPUT BETWEEN { $LOWER, $UPPER }

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"number BETWEEN {1,5}"];

 

3.字符串比较

字符串比较在默认的情况下是区分大小写和音调的。在方括号中用关键字符c和d来修改操作,比如firstname BEGINSWITH[cd] $FIRST_NAME。

[c]不区分大小写 , [d]不区分发音符号即没有重音符号 , [cd]既不区分大小写,也不区分发音符号。

  • BEGINSWITH:左边的表达式以右边的表达式作为开始。
  • CONTAINS:左边的表达式包含右边的表达式。
  • ENDSWITH:左边的表达式以右边的表达式作为结束。
  • LIKE左边的表达式等于右边的表达式:?*可作为通配符,其中?匹配1个字符,*匹配0个或者多个字符。
  • MATCHES正则表达式:左边的表达式根据ICU v3(更多内容请查看ICU User Guide for Regular Expressions)的regex风格比较,等于右边的表达式。
  • SELF :字符串本身,例:@"SELF == 'APPLE'"

//name 均是对象的属性
[NSPredicate predicateWithFormat:@"name CONTAINS[cd] 'xxx'"];   //包含某个(xxx)字符串 
     
[NSPredicate predicateWithFormat:@"name BEGINSWITH[c] 'xxx'"];     //以某个字符串开头    
  
[NSPredicate predicateWithFormat:@"name ENDSWITH[d] 'xxx'"];      //以某个字符串结束

[NSPredicate predicateWithFormat:@"name LIKE[cd] '*er*'"];     //*代表通配符,Like也接受[cd].     
[NSPredicate predicateWithFormat:@"name LIKE[cd] '???er*'"]; 

NSString *regex = @"^A.+e$";   //以A开头,e结尾
[NSPredicate predicateWithFormat: @"name MATCHES %@",regex]; 
     

//在数组筛选中,如果我不想用任何实例方法,想筛选成员本身时,可以用self来代替
NSArray *array = @[@"jim", @"cook", @"jobs", @"APPLE"];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"SELF == 'APPLE'"];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);

 

4.基本复合谓词

  • AND&&:逻辑.
  • OR||:逻辑.
  • NOT!:逻辑.

 

5.合计操作

  5.1关系操作

  • ANYSOME:指定下列表达式中的任意元素。比如,ANY children.age < 18
  • ALL:指定下列表达式中的所有元素。比如,ALL children.age < 18
  • NONE:指定下列表达式中没有的元素。比如,NONE children.age < 18。它在逻辑上等于NOT (ANY ...)
  • IN等于SQL的IN操作,左边的表达必须出现在右边指定的集合中。比如,name IN { 'Ben', 'Melissa', 'Nick' }

  5.2数组操作

  • array[index]:指定数组中特定索引处的元素。
  • array[FIRST]:指定数组中的第一个元素。
  • array[LAST]:指定数组中的最后一个元素。
  • array[SIZE]指定数组的大小

  5.3布尔值谓词

  • TRUEPREDICATE:结果始终为的谓词。
  • FALSEPREDICATE:结果始终为的谓词。

    

三、应用场景

1.集合中使用NSPredicate

  Foundation提供使用谓词(predicate)来过滤NSArrayNSMutableArray&NSSetNSMutableSet的方法。

  对不可变的集合(NSArray&NSSet),通过评估接收到的predicate,用 `filteredArrayUsingPredicate:` 或 `filteredSetUsingPredicate:` 方法来返回一个不可变集合:

filteredArrayUsingPredicate: 是NSArray数组的一种类别方法,循环过滤数组中的内容,将值为YES的对象累积到结果数组中返回。

//对NSArray进行过滤 
NSArray *array = [[NSArray alloc]initWithObjects:@"beijing",@"shanghai",@"guangzou",@"wuhan", nil];    
NSString *string = @"ang";    
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@",string];    
NSLog(@"%@",[array filteredArrayUsingPredicate:pred]);  

 

  对可变集合(NSMutableArray&NSMutableSet),可以使用方法filterUsingPredicate:,它可以通过运行接收到的谓词来移除评估结果为FALSE的对象。

 

  NSDictionary可以用谓词来过滤它的键和值(两者都为NSArray对象)。NSOrderedSet可以由过滤的NSArrayNSSet生成一个新的有序的集,或者NSMutableSet可以简单的removeObjectsInArray:,来传递通过_否定_predicate过滤的对象。

注意在筛选过程中,要灵活利用成员实例方法,如:lenght、integerValue

NSArray *array = @[@"jim", @"cook", @"jobs", @"sdevm"];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"length > 3"];//lenght就是对数组成员执行[xxxx lenght]然后判断返回的NSUInteger值是否大于3
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
//结果:( cook, jobs, sdevm )

//扩展到NSString其他方法又比如integerValue:
NSArray *array = @[@"2", @"3", @"4", @"5"];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"integerValue >= %@", @3];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);

如果我不想用任何实例方法,想筛选成员本身应该怎么做呢?这时候就可以用self来代替:

NSArray *array = @[@"2", @"3", @"4", @"5"];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"self CONTAINS %@", @3];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);

 

2.模型中使用NSPredicate ---  由集合拓展到模型

  由`利用成员实例方法(集合中成员的方法)`拓展到`利用成员方法(集合中对象的属性的方法)`

  筛选出数组成员方法[people firstName](firstName属性的get方法)返回值 = 'Bob' 的成员。

NSPredicate *bobPredicate = [NSPredicate predicateWithFormat:@"firstName = 'Bob'"];

// ["Bob Jones"]
NSLog(@"Bobs: %@", [people filteredArrayUsingPredicate:bobPredicate]);

 

3.多重筛选NSCompoundPredicate

  如果需要匹配数个属性的筛选,用AND或者OR来串联显然有点麻烦。

  NSCompoundPredicate类可以满足我们的需求,它可以将多个NSPredicate对象的组合,组合方式可以是AND或者OR

  andPredicateWithSubpredicates:    orPredicateWithSubpredicates:

   NSPredicate *pre1 = [NSPredicate predicateWithFormat:@"code >= %@", @3];
    NSPredicate *pre2 = [NSPredicate predicateWithFormat:@"code <= %@", @2];

    //以AND形式组合
    NSPredicate *pre = [NSCompoundPredicate andPredicateWithSubpredicates:@[pre1,pre2]];
    //以OR形式组合
    NSPredicate *pre = [NSCompoundPredicate orPredicateWithSubpredicates:@[pre1, pre2]];

 

4.匹配用法evaluateWithObject:

  其实NSPredicate不仅可以用于筛选,还可以用来判断匹配直接返回是否符合。

  主要方法是 -(BOOL)evaluateWithObject:(id)object;

    Test *test1 = [[Test alloc]init];
    test1.name = @"absr";
    test1.code = @1;

    NSPredicate *pres = [NSPredicate predicateWithFormat:@"code == %@", @2];
    BOOL match = [pres evaluateWithObject:test1];

  当然最常用的还是配合配合正则表达式,列举几个常用的正则:

  • 是否以a开头以e结尾
    NSString *string=@"assdbfe";
    NSString *targetString=@"^a.+e$";
    NSPredicate *pres = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", targetString];
    BOOL match = [pres evaluateWithObject:string];
  • 是否是邮箱
NSString *strRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{1,5}"; 
  • 是否是手机号
NSString *strRegex = @"[0-9]{1,20}";  

 

5.Core Data中使用NSPredicate

NSFetchRequest有一个predicate属性,它可以指定管理对象应该被获取的逻辑条件。谓词的使用规则在这里同样适用,唯一的区别在于,在管理对象环境中,谓词由持久化存储助理(persistent store coordinator)评估,而不像集合那样在内存中被过滤。

    //查询
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entityName];
    
    //条件查询
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@""];
    request.predicate = predicate;

    //执行查询
    NSArray *objectArray = [self.managedObjectContext executeFetchRequest:request error:nil];
    
    if (![objectArray count]) {
        NSLog(@"未找到符合条件的对象!");
    }
    
    return objectArray;

 

四、Block谓词 --- 另一种创建NSPredicate的方式

最后,如果你实在不愿意学习NSPredicate的格式语法,你也可以学学NSPredicate +predicateWithBlock:

NSPredicate *shortNamePredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
            return [[evaluatedObject firstName] length] <= 5;
        }];

// ["Alice Smith", "Bob Jones"]
NSLog(@"Short Names: %@", [people filteredArrayUsingPredicate:shortNamePredicate]);

...好吧,虽然使用predicateWithBlock:是懒人的做法,但它也并不是一无是处。

 

事实上,因为block可以封装任意的计算,所以有一个查询类是无法以NSPredicate格式字符串形式来表达的(比如对运行时被动态计算的值的评估)。而且当同一件事情可以用NSExpression结合自定义选择器来完成时,block为完成工作提供了一个方便的接口。

 

重要提示:由predicateWithBlock:生成的NSPredicate不能用于由SQLite存储库支持的Core Data数据的提取要求。

 

本文参考自NSHipster.cnbawn

 

posted on 2016-03-25 15:40  Wilson_CYS  阅读(629)  评论(0编辑  收藏  举报

导航