NSPredicate是基础库中用来过滤获取数据的类,相似于SQL中的where
语句,但它提供了更为天然且高级的语言,在数据集合的查询上去定义了逻辑条件语句。git
直接展现NSPredicate的用法可能比抽象的讲解更容易理解,但在这以前,咱们先来学习其基础的语法。正则表达式
谓词格式字符串对关键字、括号敏感,对空格不明感,且不会进行语义类型检测。express
$ :以$符号开头声明变量,例如 $VARIABLE_NAME
数组
%K :属性名性能优化
%@ :属性值bash
使用%@占位符只能表示一些表达式,而不能去表示一个谓词,不然会形成程序异常。less
=、== : 判断左边表达式是否等于右边表达式。ide
>=、 => : 判断左边表达式是否大于或等于右边表达式。性能
<=、=< : 判断左边表达式是否小于或等于右边表达式。学习
> : 判断左边表达式是否大于右边表达式。
< : 判断左边表达式是否小于右边表达式。
!=、<> : 判断左边表达式是否不等于右边表达式。
BETWEEN : 判断值是否在区间内,形如 $INPUT BETWEEN { $LOWER, $UPPER }
TRUEPREDICATE : 始终返回为真的谓词。
FALSEPREDICATE : 始终返回为假的谓词。
AND、&& : 逻辑与。
OR、|| : 逻辑或。
NOT、! : 逻辑非。
字符串默认对字母大小写和发音敏感,但咱们能够手动对其进行关闭。例如 firstName BEGINSWITH[cd] $FIRST_NAME
。c、d分别对应关闭对字母大小写与发音的敏感。
BEGINSWITH : 判断左边字符串内容是否以右边字符串开始。
CONTAINS : 判断字符串内容是否包含右边字符串。
ENDSWITH : 判断字符串内容是否以右边字符串结束。
LIKE : 判断左边字符串是否等于右边字符串。? 和 * 一般在这里被用做通配符。
? : 表示匹配一个字符。
* : 表示匹配0个或多个字符。
注意:与通配符 ? 或 * 的组合必须是以字符串的形式表示。
MATCHES : 经过正则表达式判断左右两边表达式是否相等。
UTI-CONFORMS-TO 与 UTI-EQUALS : 这两个属于macOS开发部分,这里不讨论。
ANY、SOME : 指定知足后面表达式的一些/部分元素。例如ANY children.age < 18
ALL : 指定知足后面表达式的全部元素。例如ALL children.age < 18
NONE : 指定不知足后面表达式的元素。例如NONE children.age < 18
。等同于NOT (ANY ...)
。
IN : 等同于SQL的IN操做。判断左边元素集合是否在右边元素集合出现。例如name IN { 'Ben', 'Melissa', 'Nick' }
。这些集合能够是array、set、或者是dictionary。若是是dictionary,将取其values值。
array[index] : 指定数组array中指定索引表明的元素。
array[FIRST] : 指定数组array的第一个元素。
array[LAST] : 指定数组array的最后一个元素。
array[SIZE] : 指定数组array的大小。
全部C风格的标识符都不被保留。
#symbol : 将保留字转义为用户标识符。
[\]{octaldigit}{3} : 转义8进制数字。(\
后跟上3个八进制数字)
[\][xX]{hexdigit}{2} : 转义16进制数字。(\x
/\X
后跟上2十六进制数字)
[\][uU]{hexdigit}{4} : 转义Unicode。(\u
/\U
后跟上4个十六进制数字)
单引号与双引号有着一样的效果,可是他们不能使对方结束。例如 "abc" 与 `abc` 是等价的,可是 "a`b`c" 却等同于 a + space + 'b' + space + c.
FALSE、NO : 逻辑假。
TRUE、YES : 逻辑真。
NULL、NIL : 表示null值。
SELF : 表示被操做的对象。
"text" : 表示一个字符串。
`text` : 表示一个字符串。
AND, OR, IN, NOT, ALL, ANY, SOME, NONE, LIKE, CASEINSENSITIVE, CI, MATCHES, CONTAINS, BEGINSWITH, ENDSWITH, BETWEEN, NULL, NIL, SELF, TRUE, YES, FALSE, NO, FIRST, LAST, SIZE, ANYKEY, SUBQUERY, FETCH, CAST, TRUEPREDICATE, FALSEPREDICATE, UTI-CONFORMS-TO, UTI-EQUALS
Creating Predicate(建立谓词)
Creating a Predicate Using a Format String
一般咱们使用NSPredicate的类方法+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;
来定义一个谓词,由于编译器是不会对字符串进行语义类型的检测,致使错误在编译时没法被察觉,因此咱们必须严格进行编写,不然在替换变量的一些状况下会形成运行时错误。
String Constants, Variables, and Wildcards(字符串常量/变量,通配符)
在谓词格式字符串中插入字符串常量,须要用到成对的单引号或者双引号且使用转义字符\
,例如NSPredicate *predicate = [NSPredicate predicateWithFormat:@"lastName like[c] \"S*\""];
在谓词格式字符串中插入占位符%@
,那么%@
表示的内容会被自动的添加上引号。这里要特别注意的是%@
不能与通配符*
或者?
直接使用,而必须使用拼接的方式。例如
NSString *prefix = @"prefix";
NSString *suffix = @"suffix";
NSPredicate *predicate = [NSPredicate
predicateWithFormat:@"SELF like[c] %@",
[[prefix stringByAppendingString:@"*"] stringByAppendingString:suffix]];
BOOL ok = [predicate evaluateWithObject:@"prefixxxxxxsuffix"];
复制代码
在谓词格式字符串中插入变量,例如
predicate = [NSPredicate
predicateWithFormat:@"lastName like[c] $LAST_NAME"];
复制代码
Boolean Values(Boolean值)
指定和测试Boolean值的相等性。例如
NSPredicate *newPredicate =
[NSPredicate predicateWithFormat:@"anAttribute == %@", [NSNumber numberWithBool:aBool]];
NSPredicate *testForTrue =
[NSPredicate predicateWithFormat:@"anAttribute == YES"];
复制代码
Dynamic Property Names(动态属性名)
咱们不能使用%@
表示属性名,由于使用%@
占位符表示后的值会被引号包裹成字符串变量。例如
NSString *attributeName = @"firstName";
NSString *attributeValue = @"Adam";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ like %@",
attributeName, attributeValue];
复制代码
该谓词格式字符串是 "firstName" LIKE "Adam"。
若是要指定一个动态属性名,则须要用到%K
。例如
predicate = [NSPredicate predicateWithFormat:@"%K like %@",
attributeName, attributeValue];
复制代码
该谓语格式字符串则变成咱们想要的 firstName LIKE "Adam"。
Creating Predicates Directly in Code(用代码定义一个谓词)
下面是一段表示(revenue >= 1000000) and (revenue < 100000000)
的代码例子。
NSExpression *lhs = [NSExpression expressionForKeyPath:@"revenue"];
NSExpression *greaterThanRhs = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:1000000]];
NSPredicate *greaterThanPredicate = [NSComparisonPredicate
predicateWithLeftExpression:lhs
rightExpression:greaterThanRhs
modifier:NSDirectPredicateModifier
type:NSGreaterThanOrEqualToPredicateOperatorType
options:0];
NSExpression *lessThanRhs = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:100000000]];
NSPredicate *lessThanPredicate = [NSComparisonPredicate
predicateWithLeftExpression:lhs
rightExpression:lessThanRhs
modifier:NSDirectPredicateModifier
type:NSLessThanPredicateOperatorType
options:0];
NSCompoundPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:
@[greaterThanPredicate, lessThanPredicate]];
复制代码
Creating Predicates Using Predicate Templates(使用谓词模板定义谓词)
这是对经过硬编码定义谓词易形成程序出错与经过代码定义谓词带来的繁琐之间折中以后的方法。例如
NSPredicate *predicateTemplate = [NSPredicate
predicateWithFormat:@"lastName like[c] \$LAST_NAME"];
复制代码
将上述定义成谓词模板为
NSExpression *lhs = [NSExpression expressionForKeyPath:@"lastName"];
NSExpression *rhs = [NSExpression expressionForVariable:@"LAST_NAME"];
NSPredicate *predicateTemplate = [NSComparisonPredicate
predicateWithLeftExpression:lhs
rightExpression:rhs
modifier:NSDirectPredicateModifier
type:NSLikePredicateOperatorType
options:NSCaseInsensitivePredicateOption];
复制代码
这时咱们能够这样来使用它
NSPredicate *predicate = [predicateTemplate predicateWithSubstitutionVariables:
[NSDictionary dictionaryWithObject:@"Turner" forKey:@"LAST_NAME"]];
复制代码
那么如今这个新的谓词将变成lastName LIKE[c] "Turner"
使用dictionary进行替换的前提是dictionary中必须包含谓词所指定变量的键值对,因此当咱们想要匹配一个null值时,咱们必须在dictionary中提供一个null值,例如
NSPredicate *predicate = [NSPredicate
predicateWithFormat:@"date = $DATE"];
predicate = [predicate predicateWithSubstitutionVariables:
[NSDictionary dictionaryWithObject:[NSNull null] forKey:@"DATE"]];
复制代码
这时谓词变成date == <null>。
利用代码直接定义一个谓词其实就是系统帮咱们将前面学习的基础语法转换成枚举供咱们选择进行建立,避免发生硬编码错误。例如options
typedef NS_OPTIONS(NSUInteger, NSComparisonPredicateOptions) {
NSCaseInsensitivePredicateOption, // 字母大小写不敏感,即[c]
NSDiacriticInsensitivePredicateOption, // 发音不敏感,即[d]
NSNormalizedPredicateOption, // 即[cd],且系统会对该选项进行性能优化。
};
复制代码
当咱们须要组合几个谓词时,使用NSPredicate的子类NSCompoundPredicate会更加方便。
Format String Summary(格式字符串小结)
被引号包裹的%@
、%K
、$VARIABLE
会被解释成字符串,于是会阻止任何替换的行为。
@"attributeName == %@" : 该谓词会检查属性名attributeNamed的值是否会等于%@所指代的值,能够是NSDate、NSNumber、NSString等。
@"%K == %@" : 该谓词会检查键%K
的值是否等于%@
的值。
@"name IN $NAME_LIST" : 该谓词模板会检查键name是否出如今变量$NAME_LIST中。
@"`name` IN $NAME_LIST" : 该谓词模板会检查字符串常量`name`是否出如今变量$NAME_LIST中。
@"$name IN $NAME_LIST" : 该谓词模板会检查变量$name是否出如今变量$NAME_LIST中。
@"%K == `%@`" : 该谓词会检查%K
的值是否等于字符串%@
的值。
Using Predicate(使用谓词)
Evaluating Predicates(执行谓词)
这一个简单的例子
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", @[@"Stig", @"Shaffiq", @"Chris"]];
BOOL result = [predicate evaluateWithObject:@"Shaffiq"];
复制代码
注意: 只有支持KVC的类才能使用谓词。
Using Predicates with Arrays(在集合中使用谓词)
数组与可变数组都支持过滤数组元素的操做。但它们是有区别的。
filteredArrayUsingPredicate:
方法将过滤获取的元素经过一个新的数组返回。filterUsingPredicate:
方法操做的对象是原数组,只有符合谓词要求的元素才会被保留下来。例如
NSMutableArray *names = [@[@"Nick", @"Ben", @"Adam", @"Melissa"] mutableCopy];
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
NSArray *beginWithB = [names filteredArrayUsingPredicate:bPredicate];
// beginWithB contains { @"Ben" }.
NSPredicate *ePredicate = [NSPredicate predicateWithFormat:@"SELF contains[c] 'e'"];
[names filterUsingPredicate:ePredicate];
// names now contains { @"Ben", @"Melissa" }
复制代码
Using Predicates with Key-Paths(经过键路径使用谓词)
例如
NSString *departmentName = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"department.name like %@", departmentName];
复制代码
若是是一对多关系,谓词结构会有些许不一样。若是想要获取名字的first name是"Matthew"的全部员工的公寓,咱们可使用ANY
:
NSPredicate *predicate = [NSPredicate predicateWithFormat:
@"ANY employees.firstName like 'Matthew'"];
复制代码
若是咱们想要知道员工工资大于必定值的员工所在的是哪些部门:
float salary = ... ;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY employees.salary > %f", salary];
复制代码
// MARK: - 字符串
let str = "hello holy! it's so cold today!"
let p01 = NSPredicate(format: "SELF CONTAINS 'cold'")
if p01.evaluate(with: str) {
print("p01: ")
}
let p02 = NSPredicate(format: "SELF LIKE[c] 'hello'")
if p02.evaluate(with: str) {
print("p02: ")
}
let p03 = NSPredicate(format: "SELF LIKE[c] '*ello'")
if p03.evaluate(with: str) {
print("p03: ")
}
let p04 = NSPredicate(format: "SELF LIKE[c] '?ello'")
if p04.evaluate(with: str) {
print("p04: ")
}
let p05 = NSPredicate(format: "SELF LIKE '?Ello*'")
if p05.evaluate(with: str) {
print("p05: ")
}
let p06 = NSPredicate(format: "SELF LIKE[c] 'hello*!'")
if p06.evaluate(with: str) {
print("p06: ")
}
let p07 = NSPredicate(format: "SELF IN %@", str)
if p07.evaluate(with: "hello") {
print("p07: ")
}
// MARK: - 集合
let alice = Person(firstName: "Alice", lastName: "Smith", age: 24, departmentName: "A")
let bob = Person(firstName: "Bob", lastName: "Jones", age: 13, departmentName: "B")
let charlie = Person(firstName: "Charlie", lastName: "Smith", age: 20, departmentName: "A")
let quentin = Person(firstName: "Quentin", lastName: "Alberts", age: 20, departmentName: "C")
let jack = Person(firstName: "Jack", lastName: "J", age: 18, departmentName: "C")
let people: NSMutableArray = [alice, bob, charlie, quentin, jack]
self.people = people
// 1. 查找lastName为Smith的人
let p1 = NSPredicate(format: "lastName = 'Smith'")
let arr1 = people.filtered(using: p1)
print("arr1: \(arr1)");
// 2. 查找firstName为某变量的人
let p2 = NSPredicate(format: "firstName = %@", "Bob")
let arr2 = people.filtered(using: p2)
print("arr2: \(arr2)")
// 3. 查找age >= 18的人
let p3 = NSPredicate(format: "age >= 18")
let arr3 = people.filtered(using: p3)
print("arr3: \(arr3)")
// 4. 使用可数数组`filter`方法修改原数组
// let p4 = NSPredicate(format: "age = 18")
// people.filter(using: p4)
// print("people: \(people)")
// 5. 查找住在公寓A的人
let p5 = NSPredicate(format: "department.name = 'A'")
let arr5 = people.filtered(using: p5)
print("arr5: \(arr5)")
// 6. 是否有人的年龄大于25
let p6 = NSPredicate(format: "ANY people.age > 25 ")
if p6.evaluate(with: self) {
print("p6: 有")
} else {
print("p6: 没有")
}
// 7. 年龄大于等于20的人
let p7 = NSPredicate { (evaluatedObject, _) -> Bool in
return (evaluatedObject as! Person).age >= 20
}
let arr7 = people.filtered(using: p7)
print("arr7: \(arr7)")
// 8. "%K == %@"
let p8 = NSPredicate(format: "%K == %@", "lastName", "Smith")
let arr8 = people.filtered(using: p8)
print("arr8: \(arr8)")
// 9.
let p9t = NSPredicate(format: "lastName = $NAME")
let p9 = p9t.withSubstitutionVariables(["NAME": "Smith"])
let arr9 = people.filtered(using: p9)
print("arr9: \(arr9)")
// 10. 大于18岁小于20岁
let lhs = NSExpression(forKeyPath: "age")
let greaterThanRhs = NSExpression(forConstantValue: 18)
let greaterP = NSComparisonPredicate(leftExpression: lhs, rightExpression: greaterThanRhs, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.greaterThan, options: NSComparisonPredicate.Options.normalized)
let lessThanRhs = NSExpression(forConstantValue: 20)
let lessP = NSComparisonPredicate(leftExpression: lhs, rightExpression: lessThanRhs, modifier: NSComparisonPredicate.Modifier.direct, type: NSComparisonPredicate.Operator.lessThan, options: NSComparisonPredicate.Options.normalized)
let p10 = NSCompoundPredicate(andPredicateWithSubpredicates: [greaterP, lessP])
let arr10 = people.filtered(using: p10)
print("arr10: \(arr10)")
// MARK: - 验证
let testPhone = "13422222222"
let phoneRegex = "^((13[0-9])|(15[^4,\\D])|(18[0,0-9]))\\d{8}$"
let p21 = NSPredicate(format: "SELF MATCHES %@", phoneRegex)
if p21.evaluate(with: testPhone) {
print("是手机号!")
}
let testEmail = "jabread007@yahoo.com"
let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
let p22 = NSPredicate(format: "SELF MATCHES %@", emailRegex)
if p22.evaluate(with: testEmail) {
print("是邮箱号!")
}
}
// 用到的两个类
class Person: NSObject {
@objc var firstName: String = ""
@objc var lastName: String = ""
@objc var age: Int = 0
@objc var department: Department
convenience init(firstName: String, lastName: String, age: Int, departmentName: String) {
self.init()
self.firstName = firstName
self.lastName = lastName
self.age = age
self.department.name = departmentName
}
override init() {
department = Department()
super.init()
}
override var description: String {
return firstName + " " + lastName
}
}
class Department: NSObject {
@objc var name: String
init(name: String = "") {
self.name = name
}
}
复制代码
Core Data
NSFetchRequest中有predicate属性,用来对数据进行过滤获取。
验证格式
主要结合正则表达式的使用。
[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}
^((13[0-9])|(15[^4,\\D])|(18[0,0-9]))\\d{8}$