美文网首页
NSPredicate的常见使用

NSPredicate的常见使用

作者: 皓似 | 来源:发表于2023-01-17 17:15 被阅读0次

    转自NSPredicate的使用
    版本:iOS13.7

    一、简介

    给NSPredicate(谓词)设定一个表达式,可评估对象是否符合该表达式,也可过滤出符合表达式的数据(NSArray、NSSet、NSOrderedSet)。
    NSPredicate有两个子类,分别是NSComparisonPredicate和NSCompoundPredicate。
    NSComparisonPredicate 比较谓词,点击NSComparisonPredicate的使用了解。
    NSCompoundPredicate 复合谓词,点击NSCompoundPredicate的使用了解。

    二、NSPredicate的API

    //通过表达式创建一个谓词实例
    //关于表达式 详见(四、谓语表达式语法)
    + (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;
    //通过表达式创建一个谓词实例
    //表达式中的%@占位符将从arguments中取值,占位符数量必须小于等于arguments元素的数量
    //详见说明1
    + (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat argumentArray:(nullable NSArray *)arguments;
    //通过表达式创建一个谓词实例
    //表达式中的%@占位符将从argList中取值,占位符数量必须小于等于argList元素的数量
    //详见说明1
    + (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat arguments:(va_list)argList;
    
    //该方法只有macos可用,暂不说明
    + (nullable NSPredicate *)predicateFromMetadataQueryString:(NSString *)queryString;
    API_AVAILABLE(macos(10.9)) API_UNAVAILABLE(ios, watchos, tvos)
    
    //创建一个结果始终为YES/NO的谓词实例
    //详见说明2
    + (NSPredicate *)predicateWithValue:(BOOL)value; 
    
    //根据回调的返回值来创建一个结果为YES/NO谓词实例
    //其中回调的evaluatedObject和bindings分别为evaluateWithObject方法的object和bindings
    //详见例1
    + (NSPredicate*)predicateWithBlock:(BOOL (^)(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings))block;
    
    //返回谓词的表达式字符串 只读
    //[NSPredicate predicateWithFormat:@"SELF CONTAINS 'world'"]的表达式为SELF CONTAINS "world"
    @property (readonly, copy) NSString *predicateFormat; 
    
    //用常量值代替变量
    //即用字典中的键值对替换用$声明的变量
    //详见说明3
    - (instancetype)predicateWithSubstitutionVariables:(NSDictionary<NSString *, id> *)variables;  
    
    //评估对象是否符合该谓词
    - (BOOL)evaluateWithObject:(nullable id)object;
    
    //评估对象是否符合该谓词
    //其中bindings字典中的元素可以代替用$声明的变量,详见predicateWithSubstitutionVariables方法的说明3
    //也可以作为回调的参数,详见predicateWithBlock方法的例1
    - (BOOL)evaluateWithObject:(nullable id)object substitutionVariables:(nullable NSDictionary<NSString *, id> *)bindings;
    
    //强制使用已安全解码的谓词以进行评估
    //当使用 NSSecureCoding编码的NSPredicate对象在安全解码时,是不能被评估的。
    //因为评估一个从存档中取出的predicate是有潜在风险的。
    //在激活评估之前,我们应该证实key paths, selectors,和其他细节来确保没有错误或恶意代码被执行。
    //一旦我们验证了predicate,我们就可以通过调用allowEvaluation来激活接收器来评估。
    - (void)allowEvaluation;
    

    说明1:以下三种初始化方法的意义相同,最后的生成的表达式都为SELF CONTAINS "world"

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS 'world'"];
    等价于
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", @"world"];
    等价于
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@"
                             argumentArray:@[@"world"]];
    //若要使用双引号" ",需要加上转义符\
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS \"world\""];
    

    说明2:当调用evaluateWithObject时,无论传入的对象是什么,只会返回YES/NO,以下

    NSPredicate *predicate = [NSPredicate predicateWithValue:YES];
    等价于
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"TRUEPREDICATE"];
    
    NSPredicate *predicate = [NSPredicate predicateWithValue:NO];
    等价于
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"FALSEPREDICATE"];
    

    说明3:输出的格式字符串为SELF CONTAINS "wor"
    实际上和NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", @"wor"];实现的效果相同

       NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS $variable"];
       //字典中的key需要和$后面声明的变量相同,可以声明多个变量,但字典中需要全部包含
       predicate = [predicate predicateWithSubstitutionVariables:@{@"variable":@"wor"}];
       NSLog(@"%@", [predicate predicateFormat]);
    
     //举例
       NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSArray * _Nullable >evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
           //通过回调的evaluatedObject和bindings来判断是否过滤
           if ([evaluatedObject containsObject:[bindings objectForKey:@"key"]]) {
               return YES;
           } else {
               return NO;
           }
       }];
       BOOL eva = [predicate evaluateWithObject:@[@"1", @"2"] >substitutionVariables:@{@"key":@"2"}];
       NSLog(@"%@", @(eva));
    输出:
    1
    

    三、NSPredicate的扩展API

    @interface NSArray<ObjectType> (NSPredicateSupport)
    
    //将数组中的每个元素进行评估,返回所有符合谓词的元素的新数组
    - (NSArray<ObjectType> *)filteredArrayUsingPredicate:(NSPredicate *)predicate; 
    
    @end
    @interface NSMutableArray<ObjectType> (NSPredicateSupport)
    
    //将数组中的每个元素进行评估,移除所有不符合谓词的元素
    - (void)filterUsingPredicate:(NSPredicate *)predicate; 
    
    @end
    

    filteredArrayUsingPredicate会返回一个新数组,原数组不会变化。
    filterUsingPredicate会直接改变原数组的内容,如有需要,请先对原数组备份。
    例:

        //选出大于10且小于50的元素
        predicate = [NSPredicate predicateWithFormat:@"SELF > 10 AND SELF < 50"];
        NSArray *array = @[@20, @30, @40, @50, @10, @5];
        NSMutableArray *muta = [array mutableCopy];
        NSLog(@"过滤前\narray = %@\n muta = %@", array, muta);
        NSArray *array1 = [array filteredArrayUsingPredicate:predicate];
        [muta filterUsingPredicate:predicate];
        NSLog(@"过滤后\narray = %@\n array1 = %@\n muta = %@", array, array1, muta);
    输出:
    过滤前
    array = (
        20,
        30,
        40,
        50,
        10,
        5
    )
     muta = (
        20,
        30,
        40,
        50,
        10,
        5
    )
    过滤后
    array = (
        20,
        30,
        40,
        50,
        10,
        5
    )
     array1 = (
        20,
        30,
        40
    )
     muta = (
        20,
        30,
        40
    )
    

    NSSet、NSMutableSet、NSOrderedSet、NSMutableOrderedSet的NSPredicate扩展API
    功能与NSArray、NSMutableArray的扩展API相同

    @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
    

    四、谓语表达式语法:表达式字符串并不会进行语义检测,必须保证正确性

    表达式语法参考简要理解 - NSPredicate

    占位符
    $:声明变量,例如 $variable,详见说明3
    %K :属性名
    %@ :属性值

       NSString *name = @"key1";
       NSString *value = @"23";
       //字典中的键为key1的值是否包含"23"
       predicate = [NSPredicate predicateWithFormat:@"key1 CONTAINS %@", value];
       NSLog(@"%@", [predicate predicateFormat]);
       predicate = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", name, value];
       NSLog(@"%@", [predicate predicateFormat]);
       eva = [predicate evaluateWithObject:@{@"key1":@"value12345"}];
       NSLog(@"%@", @(eva));
    输出:
    key1 CONTAINS "23"
    key1 CONTAINS "23"
    1
    

    可以看到两种初始化的表达式字符串都是key1 CONTAINS "23"
    %@表示的是"23"这个带双引号的值,%K表示的是不带引号key1
    %K好像不能表示关键字,如SELF等

    基本比较运算
    =、== 左边是否等于右边
    =、 => 左边是否大于或等于右边
    <=、=< 左边是否小于或等于右边
    左边是否大于右边
    < 左边是否小于右边
    !=、<> 左边是否不等于右边
    BETWEEN 左边的值是否在区间内(包含边界数值)

    例如@"SELF BETWEEN {10, 50}"等价于@"SELF >= 10 AND SELF <= 50"

    布尔值谓语
    TRUEPREDICATE 始终返回真
    FALSEPREDICATE 始终返回假

    例如[NSPredicate predicateWithValue:YES]的表达式实际上就是TRUEPREDICATE

    基本复合谓语
    AND、&& 与
    OR、|| 或
    NOT、!: 非

    例如[NSPredicate predicateWithFormat:@"SELF > 10 AND SELF < 50"]表示大于10并且小于50
    [NSPredicate predicateWithFormat:@"SELF > 10 or SELF < 5"]表示大于10或者小于5
    [NSPredicate predicateWithFormat:@"NOT SELF > 10"]表示不大于10

    字符串比较
    BEGINSWITH 左边字符串是否以右边字符串开始
    ENDSWITH 左边字符串是否以右边字符串结束
    CONTAINS 左边字符串内容是否包含右边字符串
    LIKE 左边字符串是否等于右边字符串
    MATCHES 左边字符串是否符合右边的正则表达式

    例如[NSPredicate predicateWithFormat:@"SELF BEGINSWITH 'world'"]表示worldxxx这种字符串,xxx表示0个或多个字符

    [NSPredicate predicateWithFormat:@"SELF ENDSWITH 'world'"]表示xxxworld这种字符串
    [NSPredicate predicateWithFormat:@"SELF CONTAINS 'world'"]表示xxxworldxxx这种字符串
    [NSPredicate predicateWithFormat:@"SELF LIKE 'world'"]表示字符串world
    [NSPredicate predicateWithFormat:@"SELF MATCHES '^1[3|4|5|7|8][0-9]{9}$'"]表示符合手机号正则表达式的字符串

    可以在关键字后面加在[cd],c表示忽略大小写,即CAFE与cafe为相同字符串;d表示忽略读音,即café与cafe为相同字符串。
    例如[NSPredicate predicateWithFormat:@"SELF CONTAINS[cd] 'CAfé'"]与[NSPredicate predicateWithFormat:@"SELF CONTAINS 'cafe'"]功能相同

    LIKE后面可以使用通配符?和,但不能在%@占位符的值中使用
    ?表示指代一个字符 表示指代0个或多个字符
    例如[NSPredicate predicateWithFormat:@"SELF LIKE '?world
    '"]表示aworldaaa这种字符串
    [NSPredicate predicateWithFormat:@"SELF LIKE %@", @"?world
    "],这种是错误写法,只能识别字符串?world,?在此处不能当成通配符

    集合操作
    ANY、SOME 是否对象的元素至少有一个满足后面的表达式
    ALL 是否对象的元素全都满足后面的表达式
    NONE 是否对象的元素全都不满足后面的表达式
    上面的三种,评估的对象(evaluateWithObject的参数)必须是NSArray或NSSet

    例如[NSPredicate predicateWithFormat:@"ANY SELF CONTAINS[cd] 'cafe'"]
    表示@[@"cafe1", @"c afe2", @"c afe3"]这种数组
    [NSPredicate predicateWithFormat:@"ALL SELF CONTAINS[cd] 'cafe'"]
    表示@[@"cafe1", @"cafe2", @"cafe3"]这种数组
    [NSPredicate predicateWithFormat:@"NONE SELF CONTAINS[cd] 'cafe'"]
    表示@[@"c afe1", @"c afe2", @"c afe3"]这种数组

    IN 左边元素是否在右边元素集合出现,集合可以是NSArray、NSSet、NSDictionary,如果是NSDictionary,将取其所有value的值的数组。

    例:
      //cafe是否在集合中
       predicate = [NSPredicate predicateWithFormat:@"SELF IN[cd] {'cafe', 'apple', 'tea'}"];
      eva = [predicate evaluateWithObject:@"cafe"];
       NSLog(@"%@", @(eva));
    输出:1
    

    当evaluateWithObject的参数为NSArray、NSSet、NSDictionary时,可使用以下写法表示单个元素。
    SELF[index],表示取对应索引的元素,index不要越界
    SELF[FIRST],表示第一个元素
    SELF[LAST],表示最后一个元素
    SELF[SIZE],表示元素的个数

       //数组的第0个元素是否在集合中
       predicate = [NSPredicate predicateWithFormat:@"SELF[0] IN[cd] {'cafe', 'apple', 'tea'}"];
       eva = [predicate evaluateWithObject:@[@"cafe", @"cafe1"]];
       NSLog(@"%@", @(eva));
    输出:1
    

    若为NSDictionary,可使用
    SELF['key']或SELF.key或key(即不要前面的SELF),表示对应key的元素

       //字典key为key1的值是否在集合中
       predicate = [NSPredicate predicateWithFormat:@"SELF.key1 IN[cd] {'cafe', 'apple', 'tea'}"];
       eva = [predicate evaluateWithObject:@{@"key1":@"apple"}];
       NSLog(@"%@", @(eva));
    输出:1
    

    字面量语义
    FALSE、NO 逻辑假
    TRUE、YES 逻辑真

       predicate = [NSPredicate predicateWithFormat:@"SELF = TRUE"];
       eva = [predicate evaluateWithObject:@(YES)];
    

    五、应用

    //PredicateModel的.h文件
    @interface PredicateModel : NSObject
    
    @property (nonatomic, strong) NSString *name;
    @property (nonatomic, strong) NSString *adress;
    @property (nonatomic, assign) NSInteger age;
    @property (nonatomic, strong) PredicateModel *subModel;
    
    @end
    
    //PredicateModel的.m文件
    @implementation PredicateModel
    
    - (NSString *)description {
        return [NSString stringWithFormat:@"name = %@ adress = %@ age = %ld subModel = (%@)",
               self.name, self.adress, self.age, self.subModel?:@"empty"];
    }
    
    @end
    
        NSArray *names = @[@"aa", @"bb", @"cc"];
        NSMutableArray *models = [NSMutableArray array];
        for (NSInteger i = 0; i < 6; i++) {
            PredicateModel *model = [[PredicateModel alloc] init];
            model.name = [NSString stringWithFormat:@"%@", names[i%3]];
            model.adress = [NSString stringWithFormat:@"adress%ld", i];
            model.age = 10+random()%5;
            if (i%2 == 1) {
                //1 3 5时将上个model添加成subModel
                model.subModel = [models lastObject];
            }
            [models addObject:model];
        }
        NSLog(@"models = %@", models);
        //查询age>12且name为aa或cc的model
        NSPredicate *modelPredicate = [NSPredicate predicateWithFormat:@"SELF.age > 12 AND (name = %@ OR name = %@)", names.firstObject, names.lastObject];
        NSArray *result = [models filteredArrayUsingPredicate:modelPredicate];
        NSLog(@"result = %@", result);
        //二级查询 查询subModel存在并且subModel的age>12的model
        modelPredicate = [NSPredicate predicateWithFormat:@"subModel != NULL AND subModel.age >12"];
        result = [models filteredArrayUsingPredicate:modelPredicate];
        NSLog(@"result = %@", result);
    输出:
    models = (
        "name = aa adress = adress0 age = 13 subModel = (empty)",
        "name = bb adress = adress1 age = 11 subModel = (name = aa adress = adress0 age = 13 subModel = (empty))",
        "name = cc adress = adress2 age = 12 subModel = (empty)",
        "name = aa adress = adress3 age = 10 subModel = (name = cc adress = adress2 age = 12 subModel = (empty))",
        "name = bb adress = adress4 age = 13 subModel = (empty)",
        "name = cc adress = adress5 age = 10 subModel = (name = bb adress = adress4 age = 13 subModel = (empty))"
    )
    result = (
        "name = aa adress = adress0 age = 13 subModel = (empty)"
    )
    result = (
        "name = bb adress = adress1 age = 11 subModel = (name = aa adress = adress0 age = 13 subModel = (empty))",
        "name = cc adress = adress5 age = 10 subModel = (name = bb adress = adress4 age = 13 subModel = (empty))"
    )
    
    

    相关文章

      网友评论

          本文标题:NSPredicate的常见使用

          本文链接:https://www.haomeiwen.com/subject/oqbzcdtx.html