IOS知识积累

简要理解 - NSPredicate

2018-10-01  本文已影响55人  JABread

简介

NSPredicate是基础库中用来过滤获取数据的类,类似于SQL中的where语句,但它提供了更为自然且高级的语言,在数据集合的查询上去定义了逻辑条件语句。

直接展示NSPredicate的用法可能比抽象的讲解更容易理解,但在这之前,我们先来学习其基础的语法。

基础语法

基础用法

Creating Predicate(创建谓词)

  1. Creating a Predicate Using a Format String

    通常我们使用NSPredicate的类方法+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;来定义一个谓词,因为编译器是不会对字符串进行语义类型的检测,导致错误在编译时无法被察觉,所以我们必须严格进行编写,否则在替换变量的一些情况下会造成运行时错误。

  1. 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]];
    
  1. 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会更加方便。

  2. 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(使用谓词)

  1. Evaluating Predicates(执行谓词)

    这一个简单的例子

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF     IN %@", @[@"Stig", @"Shaffiq", @"Chris"]];
    BOOL result = [predicate evaluateWithObject:@"Shaffiq"];
    

    注意: 只有支持KVC的类才能使用谓词。

  2. Using Predicates with Arrays(在集合中使用谓词)

    数组与可变数组都支持过滤数组元素的操作。但它们是有区别的。

    • NSArray: 使用filteredArrayUsingPredicate:方法将过滤获取的元素通过一个新的数组返回。
    • NSMutableArray: 使用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" }
    
  3. 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
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读