首页 > 代码库 > iOS中的谓词NSPredicate的使用

iOS中的谓词NSPredicate的使用

  今天写一篇关于iOS中关于谓词一些用法,我们先来看苹果官方给出的解释:

  

  The NSPredicate class is used to define logical conditions used to constrain a search either for a fetch or for in-memory filtering.

  You use predicates to represent logical conditions, used for describing objects in persistent stores and in-memory filtering of objects. Although it is common to create predicates directly from instances of NSComparisonPredicate, NSCompoundPredicate, and NSExpression, you often create predicates from a format string which is parsed by the class methods on NSPredicate. Examples of predicate format strings include:

  • Simple comparisons, such as grade == "7" or firstName like "Shaffiq"
  • Case and diacritic insensitive lookups, such as name contains[cd] "itroen"
  • Logical operations, such as (firstName like "Mark") OR (lastName like "Adderley")
  • In OS X v10.5 and later, you can create ??between?? predicates such as date between {$YESTERDAY, $TOMORROW}.

You can create predicates for relationships, such as:

  • group.name like "work*"
  • ALL children.age > 12
  • ANY children.age > 12

  You can create predicates for operations, such as @sum.items.price < 1000. For a complete syntax reference, refer to the Predicate Programming Guide.

  You can also create predicates that include variables, so that the predicate can be pre-defined before substituting concrete values at runtime. In OS X v10.4, for predicates that use variables, evaluation is a two step process (see predicateWithSubstitutionVariables: and evaluateWithObject:). In OS X v10.5 and later, you can use evaluateWithObject:substitutionVariables:, which combines these steps.

   定义一个谓词的用途是通过定义一个逻辑的条件来过滤信息。

   下面我们来通过代码的方式来看一些谓词的使用。

   下面我们通过定义一个汽车的类来了解谓词。

   汽车类car的头文件定义如下: 

#import <Foundation/Foundation.h>

@class Engine;
@class Tire;

@interface Car : NSObject

@property (nonatomic, copy) Engine *engine;
@property (nonatomic, copy) NSMutableArray *tires;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *make;//制造厂商
@property (nonatomic, copy) NSString *model;
@property (nonatomic, assign) int modelYear;
@property (nonatomic, assign) int numberOfDoors;
@property (nonatomic, assign) float mileage;

- (void)setTire:(Tire*)tire atIndex:(int)index;
@end

   轮胎类头文件定义:

#import <Foundation/Foundation.h>

@interface Engine : NSObject<NSCopying>

@property (nonatomic, assign) NSInteger horsepower;//马力
@end

  车库类头文件的定义:

#import <Cocoa/Cocoa.h>
@class Car;
@interface Garage : NSObject
{
    NSString *name;
    NSMutableArray *cars;
}
@property (readwrite, copy) NSString *name;
@property (nonatomic, copy) NSMutableArray *cars;
- (void) addCar: (Car *) car;
- (void) print;
@end // Garage

 

  轮胎类头文件的定义:

#import <Foundation/Foundation.h>

@interface Tire : NSObject

@end

 

  下面是mian函数中的一些初始化的一些定义:

#import <Foundation/Foundation.h>

#import "Car.h"
#import "Tire.h"
#import "Engine.h"
#import "Garage.h"

//下面的方法的作用是重新构造一辆汽车 Car
*makeCar (NSString *name, NSString *make, NSString *model, int modelYear, int numberOfDoors,float mileage, int horsepower) { Car *car = [[[Car alloc] init] autorelease]; car.name = name; car.make = make; car.model = model; car.modelYear = modelYear; car.numberOfDoors = numberOfDoors; car.mileage = mileage; Engine *engine = [[[Engine alloc] init] autorelease]; [engine setValue: [NSNumber numberWithInt: horsepower] forKey: @"horsepower"]; car.engine = engine; // Make some tires. for (int i = 0; i < 4; i++) { Tire * tire= [[[Tire alloc] init] autorelease]; [car setTire: tire atIndex: i]; } return (car); } // makeCar

//接下来在mian函数中定义一个车库和里面存放的汽车 int main(int argc, const char * argv[]) { Garage *garage = [[Garage alloc] init]; garage.name = @"Joe’s Garage"; Car *car; car = makeCar (@"Herbie", @"Honda", @"CRX", 1984, 2, 110000, 58); [garage addCar: car];
  

   car = makeCar (@"Badger", @"Acura", @"Integra", 1987, 5, 217036.7, 130);

   [garage addCar: car];

  car = makeCar (@"ElvIs", @"Acura", @"Legend", 1989, 4, 28123.4, 151);
  [garage addCar: car];
  
  car = makeCar (@"Phoenix", @"Pontiac", @"Firebird", 1969, 2, 85128.3, 345);  

  [garage addCar: car];
  
  car = makeCar (@"Streaker", @"Pontiac", @"Silver Streak", 1950, 2, 39100.0, 36);
  [garage addCar: car];
  car = makeCar (@"Judge", @"Pontiac", @"GTO", 1969, 2, 45132.2, 370);
  [garage addCar: car];
  car = makeCar (@"Paper Car", @"Plymouth", @"Valiant", 1965, 2, 76800, 105);
  [garage addCar: car];
  car = makeCar (@"Herbie", @"Honda", @"CRX", 1984, 2, 34000, 58);
  [garage addCar: car];
  [garage print];
}

  

  以下谓词的使用都是在main函数中定义的。

  在没有谓词情况下,要判断创建的辆车名是否是叫Herbie的呢?我们可能会使用下面的代码:

    if ([car.name isEqualToString:@"Herbie"]) {
        NSLog(@"Match");
    }else{
        NSLog(@"NotMatch");
    }

  

  下面我们来看一下使用谓词的情况下应该怎么做?   

//    基本的谓词用法,创建了一个谓词,这个谓的判断条件是汽车的name Herbie相同

//    需要注意的是,如果不使用单引号的话,谓词格式将会把字符串理解成keyPath,如果使用 (keyPath是KVC的使用)

//    单引号括起来的话,谓词会理解为字符串

       

 

  NSPredicate *predicate;
  //谓词的初始化
   predicate = [NSPredicate predicateWithFormat:@"name==‘Herbie‘"];
  //谓词的判断条件 (返回一个Boolen值来判断这个对象是否满足上面的谓词条件)
   BOOL match = [predicate evaluateWithObject:car];

    if (match) {
      NSLog(@"Match");
   }else
  
  {        NSLog(@"NotMatch");    }

  看到上面的代码有人会想,这样的方法不是没有上面的代码简单吗?我们来看谓词的其它使用情况

  

    predicate = [NSPredicate predicateWithFormat:@"engine.horsepower > 150"];
    match = [predicate evaluateWithObject:car];
    if (match) {
        NSLog(@"car‘s engine horsepower > 150");
    }else
    {
        NSLog(@"car‘s engine horsepower no > 150");
    }

   这样是不是看到谓词的好处了,如果在不使用谓词的情况下,就应该先找到发动机,然后再找发动机马力大于150的,是不是感到繁琐。能过使用谓词就可以一步到位找到。

 

  我们继续来看谓词的其它用法

  //    现在要实现一个功能:将车库里的汽车,凡是发动机马力大于150的全部列出来

   //    在没有谓词的情况下:1、取出车库里的汽车

              2、创建一个可变数据用于存放满足条件的汽车

                3、遍历车库里的每一辆汽车,判断是否满足条件

              4、如果满足的话,将这辆汽车放到可变数组里。

  下面我们来对比一下使用谓词和不使用谓词来实现上述功能 

//不使用谓词的情况 
 
//该方法是从车库中取出所有的汽车,定义在Garage.m中

  NSArray *cars = [garage cars];

   
  NSMutableArray *favCars = [[NSMutableArray alloc] initWithCapacity:3]; for (Car *favCar in cars) { if (favCar.engine.horsepower > 150) { [favCars addObject:favCar]; } } NSLog(@"my fav car is %@",favCars);
 

//同上的功能,在有谓词的情况下只需要1、创建一个谓词,条件是汽车马力大于150,2、使用谓词得到结果
//定义一个谓词 满足engine.horsepower > 150

  predicate = [NSPredicatepredicateWithFormat:@"engine.horsepower > 150"];

  //通过谓词给出的条件过滤,

 NSArray *myFavCars = [cars filteredArrayUsingPredicate:predicate];

   NSLog(@"myFavCars is %@",myFavCars);

 

   谓词也可以在外界传递参数

    int horPower = 50;
    predicate = [NSPredicate predicateWithFormat:@"engine.horsepower > %d",horPower];
    myFavCars = [cars filteredArrayUsingPredicate:predicate];
    NSLog(@"%@",myFavCars);
    
    NSString *carName = @"Herbie";
    predicate = [NSPredicate predicateWithFormat:@"name == %@",carName];
    myFavCars = [cars filteredArrayUsingPredicate:predicate];
    NSLog(@"%@",myFavCars);

  

  在谓词里有一个格式说明符叫%K(大写),用于表示keyPath

    NSString *keyPath = @"name";
    NSString *condition = @"Herbie";
    predicate = [NSPredicate predicateWithFormat:@"%K == %@",keyPath,condition];
    myFavCars = [cars filteredArrayUsingPredicate:predicate];
    NSLog(@"xxxxxx%@",myFavCars);

  

 谓词里还有一个比较强大的功能就是占位符($)的使用。注意在使用占位符的时候后面$后面的属性要大写,在使用的时候也要用和定义的时候一样的大写。

  NSPredicate *predicateTemplate = [NSPredicate predicateWithFormat:@"name == $NAME"];
    
    NSDictionary *varDict = [NSDictionary dictionaryWithObjectsAndKeys:@"Herbie",@"NAME", nil];
    
    predicate = [predicateTemplate predicateWithSubstitutionVariables:varDict];
    
    myFavCars = [cars filteredArrayUsingPredicate:predicate];
    NSLog(@"-----%@",myFavCars);
    
    
    predicateTemplate = [NSPredicate predicateWithFormat:@"engine.horsepower > $POWER"];
    varDict = @{@"POWER":@150};
    predicate = [predicateTemplate predicateWithSubstitutionVariables:varDict];
    myFavCars = [cars filteredArrayUsingPredicate:predicate];
    NSLog(@"+++++++%@",myFavCars);

 

  谓词的使用还可以用一些比较运算符和逻辑运算符 如:> < >= <= != <> 比较运算符

  逻辑运算符与或非相对应的标识符 AND OR(逻辑运算符全部要大写)

    predicate = [NSPredicate predicateWithFormat:@"(engine.horsepower  < 59) AND (engine.horsepower > 200)"];
    myFavCars = [cars filteredArrayUsingPredicate:predicate];
    [garage print];
    NSLog(@"50-200 %@",myFavCars);
    
    
    predicate = [NSPredicate predicateWithFormat:@"name < ‘Newton‘"];
    myFavCars = [cars filteredArrayUsingPredicate:predicate];
    NSLog(@"%@",myFavCars);

 

  谓词也可以直接使用硬编码的格式也可以不直接使用,占位符也可以使用

  

//    直接使用硬编码的方式来表示两个数据之间的值的覆盖件

    predicate = [NSPredicatepredicateWithFormat:@"engine.horsepower BETWEEN {50,200}"];

    myFavCars = [cars filteredArrayUsingPredicate:predicate];

    NSLog(@"%@",myFavCars);

   //使用变量来表示两个数据值之间的值的条件

  //定义表示范围的值 NSArray *betweens = @[@50,@200]; predicate = [NSPredicatepredicateWithFormat:@"engine.horsepower BETWEEN %@",betweens]; myFavCars = [cars filteredArrayUsingPredicate:predicate]; NSLog(@"%@",myFavCars);

 

     //占位符来表两个数据之间的值的条件。

     predicateTemplate = [NSPredicatepredicateWithFormat:@"engine.horsepower BETWEEN $POWERS"];

     varDict = @{@"POWERS" : @[@50,@200]};

      predicate = [predicateTemplate predicateWithSubstitutionVariables:varDict];

    

      myFavCars = [cars filteredArrayUsingPredicate:predicate];

      NSLog(@"%@",myFavCars);

 

  谓词的使用还可以使用集合的概念的情况:(IN)

  

//    创建一个谓词对象, 使用这个对象,查找出车库里车名为Herbie Snugs Badger Flag的汽车的信息
//  就是把名字为下面的全部取出来
predicate = [NSPredicate predicateWithFormat:@"name IN {‘Herbie‘,‘Snugs‘,‘Badger‘,‘Flag‘}"]; myFavCars = [cars filteredArrayUsingPredicate:predicate]; NSLog(@"%@",myFavCars);
// SELF 在这里表示的是使谓词进行最终计算的时候的对象,实际就是cars数组里的car对象。 predicate = [NSPredicate predicateWithFormat:@"SELF.name IN {‘Herbie‘,‘Snugs‘,‘Badger‘,‘Flag‘}"]; myFavCars = [cars filteredArrayUsingPredicate:predicate]; NSLog(@"%@",myFavCars); // 想把车库里的所有车的名字取出来,使用kvc就可以完成集合的整体操作 NSArray *carNames = [cars valueForKey:@"name"]; NSLog(@"carNames is %@",carNames); predicate = [NSPredicate predicateWithFormat:@"SELF IN {‘Herbie‘,‘Snugs‘,‘Badger‘,‘Flag‘}"]; myFavCars = [carNames filteredArrayUsingPredicate:predicate]; NSLog(@"%@",myFavCars); // 需要注意的是, 在谓词格式里面,需要遵守一个规则, 就是关键字大写 // 查找以下两个车库的相同名字的汽车 // {‘Herbie‘,‘Snugs‘,‘Badger‘,‘Flag‘} {‘Judge‘,‘Paper Car‘ ,‘Badger‘,‘Phoenix‘}
// 该方法的作用是比较在两个数组里面共有名字,然后取出来(相当于集合中的取交集)
NSArray *names1 = @[@"Herbie",@"Snugs",@"Badger",@"Flag"]; NSArray *names2 = @[@"Judge",@"Paper Car" ,@"Badger",@"Phoenix"]; predicate = [NSPredicate predicateWithFormat:@"SELF IN %@",names1]; myFavCars = [names2 filteredArrayUsingPredicate:predicate]; NSLog(@"%@",myFavCars);

  在谓词的使用中也可以使用通配符。下面我们来看一下通配符在谓词中的使用。

//    从当前的车库里,找到名字中包含有i字母的汽车
    
//  这个方法中的作用是把名字中的包含i字母的名字取出来,关键字用的是CONTAINS[cd]其中[]中的c的意思是忽略字母的大小写,d是跟字母的重音有关。
// predicate = [NSPredicate predicateWithFormat:@"SELF.name CONTAINS[cd] %@",@"i"];
//  这个方法使用的是LIKE,下面的判断是名字中含有字母vi。
// predicate = [NSPredicate predicateWithFormat:@"SELF.name LIKE[c] ‘*vi*‘"];

//下面这个也是通配符的使用,使用关键字MATCHES.判断心字母H开头,并且以字母i结束。 predicate = [NSPredicate predicateWithFormat:@"SELF.name MATCHES ‘H[a-z]*i?‘"]; myFavCars = [cars filteredArrayUsingPredicate:predicate]; NSLog(@"%@",myFavCars);

  当然了,也可以在谓词中使用运算符,像SUM来求和之类的,有兴趣的大家可以测试一下,关于谓词的使用就先介绍到这里。