Objective-C中的NSPredicate

cact 8年前
   <p>编写软件时,经常需要获取一个对象集合,并通过某些已经条件计算该集合的值。你需要保留符合某个条件的对象,删除那些不满足条件的对象,从而提供一些有意义的对象。</p>    <p>在使用软件iPhoto的过程中,经常会看到这种现象,如果通知iPhoto仅显示等级为三星级或三星级以上的图片,则指定的条件为“照片的等级必须为三星级或三星级以上”。这样,所有照片都需要经过该过滤器过滤。满足条件的对象通过了过滤器,而其他对象被筛除了。最后,iPhoto将显示出所有高质量的图片。</p>    <p>Cocoa提供了一个名为NSPredicate的类,它用于指定过滤器的条件。可以创建NSPredicate对象,通过该对象准确地描述所需的条件,对每个对象通过谓词进行筛选,判断它们是否与条件相匹配。这里的“谓词”通常用在数学和计算机科学概念中,表示计算真值或假值的函数。</p>    <p>Cocoa用NSPredicate描述查询的方式,原理类似于在数据库中进行查询。可以在数据库风格的API中使用NSPredicate类,例如Core Data和Spotlight。可以将NSPredicate看成另一种间接操作方式。例如,如果需要查询满足条件的机器人,可以使用谓词对象进行检查,而不必使用代码进行显示查询。通过交换谓词对象,可以使用通用代码对数据进行过滤,而不必对相关条件进行硬编码。</p>    <h2>创建</h2>    <p>1)方式一</p>    <p>创建许多对象,并将它们组合起来。如果正在构建通用用户接口来指定查询,采用这种方式比较简单。</p>    <p>2)方式二</p>    <p>查询代码中的字符串。</p>    <pre>  <code class="language-objectivec">Car *car;  Car = makeCar(@"Herbie", @"Honda", @"CRX",  1984, 2, 110000, 58);  [garage addCar:car];  /*构建的汽车:品牌为Herbie,型号为双门1984Honda CRX,马力引擎为58,已经行驶距离为110000英里*/  /*创建谓词*/  NSPredicate *predicate;  predicate = [NSPredicate predicateWithFormat:@"name == 'Herbie'"];  </code></pre>    <p>计算谓词</p>    <pre>  <code class="language-objectivec">BOOL match = [predicate evaluateWithObject:car];  NSLog(@"%s",(match) ? "YES":"NO");  </code></pre>    <p>另外一个谓词:</p>    <pre>  <code class="language-objectivec">NSPredicate *predicate = [NSPredicate predicateWithFormat:@"engine.horsepower >150"];  /*查看车库中哪些车的功率最大,可以循环测试每个汽车的谓词*/  NSArray *cars = [garage cars];  for(Car *car in cars){      if([predicate evaluateWithObject:car]){          NSLog(@"%@", car.name);      }  }  </code></pre>    <h2>过滤器</h2>    <p>如果我们不必像上文那样编写for循环和if语句,这有什么不好?实际上,某些类别将谓词过滤方法添加到了Cocoa集合类中。 -filteredArrayUsingPredicate: 是NSArray数组中的一种类别方法,它将循环过滤数组内容,根据谓词计算每个对象的值,并将值为YES的对象累积到将被返回的新数组中:</p>    <pre>  <code class="language-objectivec">NSArray *results;  results = [cars filteredArrayUsingPredicate:predicate];  NSLog(@"%@",results);  </code></pre>    <p>假如有一个可变数组,你需要剔除不属于该数组的所有项目:</p>    <pre>  <code class="language-objectivec">NSMutableArray *carsCopy = [cars mutableCopy];  [carsCopyfilterUsingPredicate:predicate];  </code></pre>    <h2>格式说明符</h2>    <p>资深编程人员都知道,硬编码并非好方法,因此,我们可以通过格式符构建谓词:</p>    <pre>  <code class="language-objectivec">NSPredicate  *predicate = [NSPredicate predicateWithFormat:@"engine.horsepower > %d", 50];  </code></pre>    <h2>运算符</h2>    <p>NSPredicate的格式字符串包含大量不同的运算符。</p>    <p>1)比较和逻辑运算符</p>    <p>谓词字符串语法支持C语言中一些常用的运算符,例如等号运算符==和=。</p>    <p>不等号运算符具有各种形式:</p>    <pre>  <code class="language-objectivec">>:大于  >=和=>:大于或等于  <:小于  <=和=<:小于或等于  !=和<>:不等于  </code></pre>    <p>此外,谓词字符串语法还支持括号表达式和AND、OR、NOT逻辑运算符或者C样式的等效表达式&&、||和!。</p>    <p>2)数组运算符</p>    <p>谓词字符串“(engine.horsepower> 50) OR (engine.horsepower < 200)”是一种十分常见的模式。等效于:</p>    <pre>  <code class="language-objectivec">predicate= [NSPredicate predicateWithFormat:@"engine.horespower BETWEEN {50,200}"];  </code></pre>    <p>花括号表示数组,BETWEEN将数组中第一个元素看成是数组的下界,第二个元素看成是数组的上界。</p>    <pre>  <code class="language-objectivec">NSArray *betweens = [NSArray arrayWithObjects:[NSNumber numberWithInt:50],[NSNumber   numberWithInt:200], nil];  predicate = [NSPredicate predicateWithFormat:@"engine.horsepower BETWEEN %@",betweens];  </code></pre>    <p>数组不仅仅用来指定某个区间的端点值。你可以使用IN运算符查找数组中是否含有某个特定值。</p>    <pre>  <code class="language-objectivec">predicate = [NSPredicate predicateWithFormat:@"name IN {'Herbie', 'Snugs', 'Badger','Flap'}"];  </code></pre>    <p> </p>    <p>来自:http://charsdavy.github.io/2017/03/28/objc-NSPredicate/</p>    <p> </p>