30分钟快速带你理解iOS中的谓词NSPredicate

一、引言

在现代汉语的解释中,谓词是用来描述或判断客体性质、特征或者客体之间关系的词项。通俗的说,它是描述事物属性的。在iOS开发Cocoa框架中,有提供NSPredicate类,这个类通常也被成为谓词类,其主要的作用是在Cocoa中帮助查询和检索,但是需要注意,实质上谓词并不是提供查询和检索的支持,它是一种描述查询检索条件的方式,就像更加标准通用的正则表达式一样。

NSPredicate提供的谓词可以分为两类:比较谓词和复合谓词。

  • 比较谓词:比较谓词通过使用比较运算符来描述所符合条件的属性状态。
  • 复合谓词:复合谓词用来组合多个比较谓词的结果,取交集,并集或补集。

对于比较谓词,可以描述精准的比较也可以通过范围或者包含等进行模糊比较。需要注意,任何Cocoa类对象都可以支持谓词,但是此类需要实现键值编码(key-value-coding)协议。

二、NSPredicate类的应用解析

NSPredicate提供创建谓词对象和解析谓词对象的方法,它也是Cocoa中有关谓词的类中的基类。我们在日常开发中,NSPredicate类的应用频率也最高。

创建谓词对象有3种方式,分别是通过格式化字符串创建谓词,直接通过代码创建谓词,通过模板创建谓词。NSPredicate提供了如下函数来进行初始化:

//通过格式化字符串来进行谓词对象的初始化
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat argumentArray:(nullable NSArray *)arguments;
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat arguments:(va_list)argList;

使用格式化字符串进行谓词的初始化十分灵活,但是需要注意,其谓词字符串的语法和正则表达式并不一样,后面会有具体的介绍,下面是一个谓词检索示例:

 //检索属性length为5的对象
 NSPredicate * predicate = [NSPredicate predicateWithFormat:@"length = 5"];
 //对于这个数组中的字符串,即是检索字符串长度为5的元素
 NSArray * test = @[@"sda",@"321",@"sf12",@"dsdwq1",@"swfas"];
 NSArray * result = [test filteredArrayUsingPredicate:predicate];
 //将打印@[@"swfas"]
 NSLog(@"%@",result);

其实,你也可以像使用NSLog函数一样来进行格式化字符串的构造,可以使用%@,%d等等格式化字符来在运行时替换为变量的实际值。同时也需要注意,这种格式化字符串创建的谓词语句并不会进行语法检查,错误的语法会产生运行时错误,要格外小心。有一个小细节需要注意,在进行格式化时,如果使用的是变量则不需要添加引号,解析器会帮助你添加,如果使用到常量,则要用转义字符进行转义,例如:

NSPredicate * predicate = [NSPredicate predicateWithFormat:@"name = %@ && age = \"25\"",name];

对于属性名,如果也需要进行格式化,需要注意不能使用%@符号,这个符号在解析时会被解析器自动添加上引号,可以使用%K,示例如下:

 NSString * key = @"length";
 NSPredicate * predicate = [NSPredicate predicateWithFormat:@"%K = 5",key];
 NSArray * test = @[@"sda",@"321",@"sf12",@"dsdwq1",@"swfas"];
 NSArray * result = [test filteredArrayUsingPredicate:predicate];
 //将打印@[@"swfas"]
 NSLog(@"%@",result);

通过模板来创建谓词对象也是一种十分常用的方式,和格式化字符串不同的是,谓词模板中只有键名,没有键值,键值需要在字典中进行提供,例如:

 NSPredicate * predicate = [NSPredicate predicateWithFormat:@"length = $LENGTH"];
 predicate = [predicate predicateWithSubstitutionVariables:@{@"LENGTH":@5}];
 NSArray * test = @[@"sda",@"321",@"sf12",@"dsdwq1",@"swfas"];
 NSArray * result = [test filteredArrayUsingPredicate:predicate];
 //将打印@[@"swfas"]
 NSLog(@"%@",result);

NSPredicate中其他属性与方法解析如下:

//创建一个总是验证通过(YES)或不通过(NO)的谓词对象
/*
如果创建的是验证通过的,则任何检索都会成功进行返回,否则任何检索都会失败不返回任何对象
*/
+ (NSPredicate *)predicateWithValue:(BOOL)value;
//自定义实现检索函数
/*

例如前面的示例也可以这样写

NSPredicate * predicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
 if ([evaluatedObject length]==5) {
 return YES;
 }
 return NO;
 }];
*/
+ (NSPredicate*)predicateWithBlock:(BOOL (^)(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings))block;
//格式化字符串属性
@property (readonly, copy) NSString *predicateFormat;
//当使用谓词模板来进行对象创建时,这个函数用来设置谓词模板中变量替换
- (instancetype)predicateWithSubstitutionVariables:(NSDictionary<NSString *, id> *)variables;
//检查一个Object对象是否可以通过验证
- (BOOL)evaluateWithObject:(nullable id)object;
//用谓词模板进行对象的验证
- (BOOL)evaluateWithObject:(nullable id)object substitutionVariables:(nullable NSDictionary<NSString *, id> *)bindings;

三、通过代码来创建谓词对象

前面我们说有3种创建谓词对象的方式,有两种我们已经有介绍,通过代码直接创建谓词对象是最复杂的一种。通过代码来创建谓词对象十分类似通过代码来创建Autolayout约束。通过前面我们的介绍,谓词实际是用表达式来验证对象,用代码来创建谓词实际就是用代码来创建表达式。

1.先来看NSComparisonPredicate类

这个类是NSPredicate的子类,其用来创建比较类型的谓词。例如使用下面的代码来改写上面的例子:

 //创建左侧表达式对象 对应为键
 NSExpression * left = [NSExpression expressionForKeyPath:@"length"];
 //创建右侧表达式对象 对应为值
 NSExpression * right = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:5]];
 //创建比较谓词对象 这里设置为严格等于
 NSComparisonPredicate * pre = [NSComparisonPredicate predicateWithLeftExpression:left rightExpression:right modifier:NSDirectPredicateModifier type:NSEqualToPredicateOperatorType options:NSCaseInsensitivePredicateOption];
 NSArray * test = @[@"sda",@"321",@"sf12",@"dsdwq1",@"swfas"];
 NSArray * result = [test filteredArrayUsingPredicate:pre];
 //将打印@[@"swfas"]
 NSLog(@"%@",result);

NSComparisonPredicateModifier用来进行条件的修饰设置,枚举如下:

typedef NS_ENUM(NSUInteger, NSComparisonPredicateModifier) {
 NSDirectPredicateModifier = 0, //直接进行比较操作
 NSAllPredicateModifier, //用于数组或集合 只有当内部所有元素都通过验证时 集合才算通过
 NSAnyPredicateModifier //同于数组或集合 当内部有一个元素满足时 集合算通过验证
};

关于NSAllPredicateModifier和NSAnyPredicateModifier,这两个枚举专门用于数组或集合类型对象的验证,ALL会验证其中所有元素,全部通过后数组或集合才算验证通过,ANY则只要有一个元素验证通过,数组或集合就算验证通过,例如:

 NSPredicate * pre = [NSPredicate predicateWithFormat:@"ALL length = 5"];
 NSArray * test = @[@[@"aaa",@"aa"],@[@"bbbb",@"bbbbb"],@[@"ccccc",@"ccccc"]];
 NSArray * result = [test filteredArrayUsingPredicate:pre];
 //将打印@[@[@"ccccc",@"ccccc"]]
 NSLog(@"%@",result);

NSPredicateOperatorType枚举用来设置运算符类型,如下:

typedef NS_ENUM(NSUInteger, NSPredicateOperatorType) {
 NSLessThanPredicateOperatorType = 0, // 小于
 NSLessThanOrEqualToPredicateOperatorType, // 小于等于
 NSGreaterThanPredicateOperatorType, // 大于
 NSGreaterThanOrEqualToPredicateOperatorType, // 大于等于
 NSEqualToPredicateOperatorType, // 等于
 NSNotEqualToPredicateOperatorType, //不等于
 NSMatchesPredicateOperatorType, //正则比配
 NSLikePredicateOperatorType, //Like匹配 与SQL类似
 NSBeginsWithPredicateOperatorType, //左边的表达式 以右边的表达式作为开头
 NSEndsWithPredicateOperatorType,//左边的表达式 以右边的表达式作为结尾
 NSInPredicateOperatorType, // 左边的表达式 出现在右边的集合中
 NSCustomSelectorPredicateOperatorType,//使用自定义的函数来进行 验证
 NSContainsPredicateOperatorType, //左边的集合包括右边的元素
 NSBetweenPredicateOperatorType //左边表达式的值在右边的范围中 例如 1 BETWEEN { 0 , 33 }
};

NSComparisonPredicateOptions枚举用来设置比较的方式,如下:

//如果不需要特殊指定 这个枚举值也可以传0
typedef NS_OPTIONS(NSUInteger, NSComparisonPredicateOptions) {
 NSCaseInsensitivePredicateOption = 0x01, //不区分大小写
 NSDiacriticInsensitivePredicateOption = 0x02,//不区分读音符号
 NSNormalizedPredicateOption //比较前进行预处理 代替上面两个选项
};

2.NSExpression类

NSExpression类则是提供创建表达式,下面列出了其中一些方便理解的方法:

//通过格式化字符串创建表达式
+ (NSExpression *)expressionWithFormat:(NSString *)expressionFormat argumentArray:(NSArray *)arguments;
+ (NSExpression *)expressionWithFormat:(NSString *)expressionFormat, ...;
+ (NSExpression *)expressionWithFormat:(NSString *)expressionFormat arguments:(va_list)argList;
//直接通过对象创建常亮的表达式
+ (NSExpression *)expressionForConstantValue:(nullable id)obj;
//创建变量表达式 验证时将从binding字典中进行替换
+ (NSExpression *)expressionForVariable:(NSString *)string;
//将多个表达式组合成一个
+ (NSExpression *)expressionForAggregate:(NSArray<NSExpression *> *)subexpressions;
+ (NSExpression *)expressionForUnionSet:(NSExpression *)left with:(NSExpression *)right;
+ (NSExpression *)expressionForIntersectSet:(NSExpression *)left with:(NSExpression *)right;
+ (NSExpression *)expressionForMinusSet:(NSExpression *)left with:(NSExpression *)right;
+ (NSExpression *)expressionForSubquery:(NSExpression *)expression usingIteratorVariable:(NSString *)variable predicate:(NSPredicate *)predicate;
//通过预定义的函数和参数数组来构建表达式对象 预定义的函数 可见dev开发文档
+ (NSExpression *)expressionForFunction:(NSString *)name arguments:(NSArray *)parameters;

3.NSCompoundPredicate类

这个类也是NSPredicate类的子类,其使用逻辑关系来组合多个谓词对象,解析如下:

//进行对象初始化
/*
typedef NS_ENUM(NSUInteger, NSCompoundPredicateType) {
 NSNotPredicateType = 0, //取非
 NSAndPredicateType, //与运算
 NSOrPredicateType, //或运算
};
*/
- (instancetype)initWithType:(NSCompoundPredicateType)type subpredicates:(NSArray<NSPredicate *> *)subpredicates;
//快速创建与运算
+ (NSCompoundPredicate *)andPredicateWithSubpredicates:(NSArray<NSPredicate *> *)subpredicates;
//快速创建或运算
+ (NSCompoundPredicate *)orPredicateWithSubpredicates:(NSArray<NSPredicate *> *)subpredicates;
//快速创建非运算
+ (NSCompoundPredicate *)notPredicateWithSubpredicate:(NSPredicate *)predicate;

四、谓词的几种使用场景

谓词主要用在验证对象,数组和集合的过滤。对象的验证前面有介绍,关于数据和集合的过滤函数,类别如下:

@interface NSArray<ObjectType> (NSPredicateSupport)
//不可变数组使用过滤器后返回新数组
- (NSArray<ObjectType> *)filteredArrayUsingPredicate:(NSPredicate *)predicate;
@end
@interface NSMutableArray<ObjectType> (NSPredicateSupport)
//可变数组可以直接进行过滤操作
- (void)filterUsingPredicate:(NSPredicate *)predicate;
@end
@interface NSSet<ObjectType> (NSPredicateSupport)
//不可变集合过滤后返回新集合
- (NSSet<ObjectType> *)filteredSetUsingPredicate:(NSPredicate *)predicate;
@end
@interface NSMutableSet<ObjectType> (NSPredicateSupport)
//可变集合可以直接进行过滤操作
- (void)filterUsingPredicate:(NSPredicate *)predicate;
@end
@interface NSOrderedSet<ObjectType> (NSPredicateSupport)
- (NSOrderedSet<ObjectType> *)filteredOrderedSetUsingPredicate:(NSPredicate *)p;
@end
@interface NSMutableOrderedSet<ObjectType> (NSPredicateSupport)
- (void)filterUsingPredicate:(NSPredicate *)p;
@end

五、谓词的格式化语法总览

下面列出了在谓词的格式化字符串规则语法。

语法规则 意义
= 左侧等于右侧
==     左侧等于右侧,与=一致 
>=     左侧大于等于右侧
=> 左侧大于等于右侧 与 >=一致
<= 左侧小于等于右侧
=<     左侧小于等于右侧 与<=一致
> 左侧大于右侧
< 左侧小于右侧
!= 左侧不等于右侧
<> 左侧不等于右侧 与!=一致
BETWEEN    左侧在右侧的集合中 key BETWEEN @[@1,@2]
TRUEPREDICATE 总是返回YES的谓词
FALSEPREDICATE 总是返回NO的谓词
AND 逻辑与
&& 逻辑与 与AND一致
OR 逻辑或
|| 逻辑或 与OR一致
NOT 逻辑非
!     逻辑非 与NOT一致
BEGINWITH 左侧以右侧字符串开头
ENDWITH 左侧以右侧字符串结尾
CONTAINS 左侧集合包含右侧元素
LIKE     左侧等于右侧 并且 *和?等通配符可以使用
MATCHES 正则匹配
ANY 对于数组集合类,验证其中任一元素
SOME 同ANY一致
ALL 对于数组集合类,验证其中所有元素
NONE 作用等同于NOT (ANY)
IN     左侧在右侧集合中
SELF 被验证的对象本身

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

您可能感兴趣的文章:

  • IOS中NSPredicate和NSRegularExpression校验正则表达式区别
(0)

相关推荐

  • IOS中NSPredicate和NSRegularExpression校验正则表达式区别

    在代码开发过程中,我们经常需要用来校验邮箱.手机号等等,这个时候就需要用到正则表达式.在iOS开发中,能用来做正则校验的有两个 NSPredicate 和 NSRegularExpression . NSPredicate NSPredicate 能用来简单做正则校验,但是它的问题是存在校验不出来的情况. //NSString+RegEx.h #import <Foundation/Foundation.h> @interface NSString (RegEx) #pragma mark -

  • 30分钟快速带你理解iOS中的谓词NSPredicate

    一.引言 在现代汉语的解释中,谓词是用来描述或判断客体性质.特征或者客体之间关系的词项.通俗的说,它是描述事物属性的.在iOS开发Cocoa框架中,有提供NSPredicate类,这个类通常也被成为谓词类,其主要的作用是在Cocoa中帮助查询和检索,但是需要注意,实质上谓词并不是提供查询和检索的支持,它是一种描述查询检索条件的方式,就像更加标准通用的正则表达式一样. NSPredicate提供的谓词可以分为两类:比较谓词和复合谓词. 比较谓词:比较谓词通过使用比较运算符来描述所符合条件的属性状态

  • 让你30分钟快速掌握vue3教程

    经过了漫长的迭代,Vue 3.0终于在上2020-09-18发布了,带了翻天覆地的变化,使用了Typescript 进行了大规模的重构,带来了Composition API RFC版本,类似React Hook 一样的写Vue,可以自定义自己的hook ,让使用者更加的灵活,接下来总结一下vue 3.0 带来的部分新特性. setup() ref() reactive() isRef() toRefs() computed() watch() LifeCycle Hooks(新的生命周期) Te

  • 反向Ajax 30分钟快速掌握

    场景1:当有新邮件的时候,网页自动弹出提示信息而无需用户手动的刷新收件箱. 场景2:当用户的手机扫描完成页面中的二维码以后,页面会自动跳转. 场景3:在类似聊天室的环境中有任何人发言,所有登录用户都可以即时看见信息. 与传统的MVC模型请求必须从客户端发起由服务器响应相比,使用反向Ajax能够模拟服务器端主动向客户端推送事件从而提高用户体验.本文将分两个部分讨论反向Ajax技术,包括:Comet和WebSocket.文章旨在演示如何实现以上两种技术手段,Struts2或SpringMVC中的应用

  • 30分钟快速入门掌握ES6/ES2015的核心内容(下)

    前言 在 30分钟掌握ES6/ES2015核心内容(上)我们讲解了es6最常用的一些语法:let, const, class, extends, super, arrow functions, template string, destructuring, default, rest arguments 俗话说打铁要趁热,今天我们继续讲es6其他几个非常有用的新特性. import export 这两个家伙对应的就是es6自己的module功能. 我们之前写的Javascript一直都没有模块化

  • 30分钟快速入门掌握ES6/ES2015的核心内容(上)

    前言 ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准.因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015. 也就是说,ES6就是ES2015. 虽然目前并不是所有浏览器都能兼容ES6全部特性,但越来越多的程序员在实际项目当中已经开始使用ES6了.所以就算你现在不打算使用ES6,但为了看懂别人的你也该懂点ES6的语法了... 在我们正式讲解ES6语法之前,我们得先了解下Babel. Babel Babel是一个广泛使用的ES6转码器,

  • 一文带你理解 Vue 中的生命周期

    目录 1.beforeCreate & created 2.beforeMount & mounted 3.beforeUpdate & updated 4.beforeDestroy & destroyed 5.activated & deactivated 前言: 每个 Vue 实例在被创建之前都要经过一系列的初始化过程.例如需要设置数据监听.编译模板.挂载实例到 DOM.在数据变化时更新 DOM 等.同时在这个过程中也会运行一些叫做生命周期钩子的函数,给予用户

  • 带你理解vue中的v-bind

    目录 一.v-bind关键源码分析 1.v-bind化的属性统一存储在哪里:attrsMap与attrsList 2.解析HTML,解析出属性集合attrs,在start回调中返回 3.在start回调中创建ASTElement,createASTElement(... ,attrs, ...) 4.创建后ASTElement会生成attrsList和attrsMap 5.attrs的数据类型定义 6.绑定属性获取函数 二.如何获取v-bind的值 1.v-bind:key源码分析 2.v-bi

  • 10分钟带你理解Java中的弱引用

    前言 本文尝试从What.Why.How这三个角度来探索Java中的弱引用,帮助大家理解Java中弱引用的定义.基本使用场景和使用方法. 一. What--什么是弱引用? Java中的弱引用具体指的是java.lang.ref.WeakReference<T>类,我们首先来看一下官方文档对它做的说明: 弱引用对象的存在不会阻止它所指向的对象被垃圾回收器回收.弱引用最常见的用途是实现规范映射(canonicalizing mappings,比如哈希表). 假设垃圾收集器在某个时间点决定一个对象是

  • 理解AngularJs篇:30分钟快速掌握AngularJs

    一.前言 对于前端系列,自然少不了AngularJs的介绍了.在前面文章中,我们介绍了如何使用KnockoutJs来打造一个单页面程序,后面一篇文章将介绍如何使用AngularJs的开发一个单页面应用程序.在开始使用AngularJs开发SPA之前,我觉得有必要详细介绍下AngularJs所涉及的知识点.所有也就有了这篇文章. 二.AngularJs介绍 AngularJS是Google推出的一款Web应用开发框架.它提供了一系列兼容性良好并可扩展的服务,包括数据绑定.DOM操作.MVC和依赖注

  • 10分钟带你理解Java中的反射

    一.简介 Java 反射是可以让我们在运行时获取类的方法.属性.父类.接口等类的内部信息的机制.也就是说,反射本质上是一个"反着来"的过程.我们通过new创建一个类的实例时,实际上是由Java虚拟机根据这个类的Class对象在运行时构建出来的,而反射是通过一个类的Class对象来获取它的定义信息,从而我们可以访问到它的属性.方法,知道这个类的父类.实现了哪些接口等信息. 二.Class类 我们知道使用javac能够将.java文件编译为.class文件,这个.class文件包含了我们对

随机推荐