轉(zhuǎn)自NSPredicate的使用
版本:iOS13.7
一聘殖、簡(jiǎn)介
給NSPredicate(謂詞)設(shè)定一個(gè)表達(dá)式楔壤,可評(píng)估對(duì)象是否符合該表達(dá)式,也可過(guò)濾出符合表達(dá)式的數(shù)據(jù)(NSArray往踢、NSSet、NSOrderedSet)。
NSPredicate有兩個(gè)子類(lèi),分別是NSComparisonPredicate和NSCompoundPredicate坷虑。
NSComparisonPredicate 比較謂詞,點(diǎn)擊NSComparisonPredicate的使用了解埂奈。
NSCompoundPredicate 復(fù)合謂詞迄损,點(diǎn)擊NSCompoundPredicate的使用了解。
二账磺、NSPredicate的API
//通過(guò)表達(dá)式創(chuàng)建一個(gè)謂詞實(shí)例
//關(guān)于表達(dá)式 詳見(jiàn)(四芹敌、謂語(yǔ)表達(dá)式語(yǔ)法)
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;
//通過(guò)表達(dá)式創(chuàng)建一個(gè)謂詞實(shí)例
//表達(dá)式中的%@占位符將從arguments中取值,占位符數(shù)量必須小于等于arguments元素的數(shù)量
//詳見(jiàn)說(shuō)明1
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat argumentArray:(nullable NSArray *)arguments;
//通過(guò)表達(dá)式創(chuàng)建一個(gè)謂詞實(shí)例
//表達(dá)式中的%@占位符將從argList中取值垮抗,占位符數(shù)量必須小于等于argList元素的數(shù)量
//詳見(jiàn)說(shuō)明1
+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat arguments:(va_list)argList;
//該方法只有macos可用氏捞,暫不說(shuō)明
+ (nullable NSPredicate *)predicateFromMetadataQueryString:(NSString *)queryString;
API_AVAILABLE(macos(10.9)) API_UNAVAILABLE(ios, watchos, tvos)
//創(chuàng)建一個(gè)結(jié)果始終為YES/NO的謂詞實(shí)例
//詳見(jiàn)說(shuō)明2
+ (NSPredicate *)predicateWithValue:(BOOL)value;
//根據(jù)回調(diào)的返回值來(lái)創(chuàng)建一個(gè)結(jié)果為YES/NO謂詞實(shí)例
//其中回調(diào)的evaluatedObject和bindings分別為evaluateWithObject方法的object和bindings
//詳見(jiàn)例1
+ (NSPredicate*)predicateWithBlock:(BOOL (^)(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings))block;
//返回謂詞的表達(dá)式字符串 只讀
//[NSPredicate predicateWithFormat:@"SELF CONTAINS 'world'"]的表達(dá)式為SELF CONTAINS "world"
@property (readonly, copy) NSString *predicateFormat;
//用常量值代替變量
//即用字典中的鍵值對(duì)替換用$聲明的變量
//詳見(jiàn)說(shuō)明3
- (instancetype)predicateWithSubstitutionVariables:(NSDictionary<NSString *, id> *)variables;
//評(píng)估對(duì)象是否符合該謂詞
- (BOOL)evaluateWithObject:(nullable id)object;
//評(píng)估對(duì)象是否符合該謂詞
//其中bindings字典中的元素可以代替用$聲明的變量,詳見(jiàn)predicateWithSubstitutionVariables方法的說(shuō)明3
//也可以作為回調(diào)的參數(shù)冒版,詳見(jiàn)predicateWithBlock方法的例1
- (BOOL)evaluateWithObject:(nullable id)object substitutionVariables:(nullable NSDictionary<NSString *, id> *)bindings;
//強(qiáng)制使用已安全解碼的謂詞以進(jìn)行評(píng)估
//當(dāng)使用 NSSecureCoding編碼的NSPredicate對(duì)象在安全解碼時(shí)液茎,是不能被評(píng)估的。
//因?yàn)樵u(píng)估一個(gè)從存檔中取出的predicate是有潛在風(fēng)險(xiǎn)的辞嗡。
//在激活評(píng)估之前豁护,我們應(yīng)該證實(shí)key paths, selectors,和其他細(xì)節(jié)來(lái)確保沒(méi)有錯(cuò)誤或惡意代碼被執(zhí)行。
//一旦我們驗(yàn)證了predicate欲间,我們就可以通過(guò)調(diào)用allowEvaluation來(lái)激活接收器來(lái)評(píng)估楚里。
- (void)allowEvaluation;
說(shuō)明1:以下三種初始化方法的意義相同,最后的生成的表達(dá)式都為SELF CONTAINS "world"
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS 'world'"]; 等價(jià)于 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", @"world"]; 等價(jià)于 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@" argumentArray:@[@"world"]]; //若要使用雙引號(hào)" "猎贴,需要加上轉(zhuǎn)義符\ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS \"world\""];
說(shuō)明2:當(dāng)調(diào)用evaluateWithObject時(shí)班缎,無(wú)論傳入的對(duì)象是什么蝴光,只會(huì)返回YES/NO,以下
NSPredicate *predicate = [NSPredicate predicateWithValue:YES]; 等價(jià)于 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"TRUEPREDICATE"]; NSPredicate *predicate = [NSPredicate predicateWithValue:NO]; 等價(jià)于 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"FALSEPREDICATE"];
說(shuō)明3:輸出的格式字符串為SELF CONTAINS "wor"
實(shí)際上和NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS %@", @"wor"];實(shí)現(xiàn)的效果相同NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF CONTAINS $variable"]; //字典中的key需要和$后面聲明的變量相同达址,可以聲明多個(gè)變量蔑祟,但字典中需要全部包含 predicate = [predicate predicateWithSubstitutionVariables:@{@"variable":@"wor"}]; NSLog(@"%@", [predicate predicateFormat]); //舉例 NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSArray * _Nullable >evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) { //通過(guò)回調(diào)的evaluatedObject和bindings來(lái)判斷是否過(guò)濾 if ([evaluatedObject containsObject:[bindings objectForKey:@"key"]]) { return YES; } else { return NO; } }]; BOOL eva = [predicate evaluateWithObject:@[@"1", @"2"] >substitutionVariables:@{@"key":@"2"}]; NSLog(@"%@", @(eva)); 輸出: 1
三、NSPredicate的擴(kuò)展API
@interface NSArray<ObjectType> (NSPredicateSupport)
//將數(shù)組中的每個(gè)元素進(jìn)行評(píng)估沉唠,返回所有符合謂詞的元素的新數(shù)組
- (NSArray<ObjectType> *)filteredArrayUsingPredicate:(NSPredicate *)predicate;
@end
@interface NSMutableArray<ObjectType> (NSPredicateSupport)
//將數(shù)組中的每個(gè)元素進(jìn)行評(píng)估疆虚,移除所有不符合謂詞的元素
- (void)filterUsingPredicate:(NSPredicate *)predicate;
@end
filteredArrayUsingPredicate會(huì)返回一個(gè)新數(shù)組,原數(shù)組不會(huì)變化满葛。
filterUsingPredicate會(huì)直接改變?cè)瓟?shù)組的內(nèi)容径簿,如有需要,請(qǐng)先對(duì)原數(shù)組備份嘀韧。
例:
//選出大于10且小于50的元素
predicate = [NSPredicate predicateWithFormat:@"SELF > 10 AND SELF < 50"];
NSArray *array = @[@20, @30, @40, @50, @10, @5];
NSMutableArray *muta = [array mutableCopy];
NSLog(@"過(guò)濾前\narray = %@\n muta = %@", array, muta);
NSArray *array1 = [array filteredArrayUsingPredicate:predicate];
[muta filterUsingPredicate:predicate];
NSLog(@"過(guò)濾后\narray = %@\n array1 = %@\n muta = %@", array, array1, muta);
輸出:
過(guò)濾前
array = (
20,
30,
40,
50,
10,
5
)
muta = (
20,
30,
40,
50,
10,
5
)
過(guò)濾后
array = (
20,
30,
40,
50,
10,
5
)
array1 = (
20,
30,
40
)
muta = (
20,
30,
40
)
NSSet篇亭、NSMutableSet、NSOrderedSet锄贷、NSMutableOrderedSet的NSPredicate擴(kuò)展API
功能與NSArray译蒂、NSMutableArray的擴(kuò)展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
四、謂語(yǔ)表達(dá)式語(yǔ)法:表達(dá)式字符串并不會(huì)進(jìn)行語(yǔ)義檢測(cè)谊却,必須保證正確性
表達(dá)式語(yǔ)法參考簡(jiǎn)要理解 - NSPredicate
占位符
$:聲明變量柔昼,例如 $variable,詳見(jiàn)說(shuō)明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
可以看到兩種初始化的表達(dá)式字符串都是key1 CONTAINS "23"
%@表示的是"23"這個(gè)帶雙引號(hào)的值炎辨,%K表示的是不帶引號(hào)key1
%K好像不能表示關(guān)鍵字岳锁,如SELF等
基本比較運(yùn)算
=、== 左邊是否等于右邊
=蹦魔、 => 左邊是否大于或等于右邊
<=激率、=< 左邊是否小于或等于右邊
左邊是否大于右邊
< 左邊是否小于右邊
!=、<> 左邊是否不等于右邊
BETWEEN 左邊的值是否在區(qū)間內(nèi)(包含邊界數(shù)值)例如@"SELF BETWEEN {10勿决, 50}"等價(jià)于@"SELF >= 10 AND SELF <= 50"
布爾值謂語(yǔ)
TRUEPREDICATE 始終返回真
FALSEPREDICATE 始終返回假例如[NSPredicate predicateWithValue:YES]的表達(dá)式實(shí)際上就是TRUEPREDICATE
基本復(fù)合謂語(yǔ)
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 左邊字符串是否以右邊字符串開(kāi)始
ENDSWITH 左邊字符串是否以右邊字符串結(jié)束
CONTAINS 左邊字符串內(nèi)容是否包含右邊字符串
LIKE 左邊字符串是否等于右邊字符串
MATCHES 左邊字符串是否符合右邊的正則表達(dá)式例如[NSPredicate predicateWithFormat:@"SELF BEGINSWITH 'world'"]表示worldxxx這種字符串嘉冒,xxx表示0個(gè)或多個(gè)字符
[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}$'"]表示符合手機(jī)號(hào)正則表達(dá)式的字符串
可以在關(guān)鍵字后面加在[cd],c表示忽略大小寫(xiě)咆繁,即CAFE與cafe為相同字符串讳推;d表示忽略讀音,即café與cafe為相同字符串玩般。
例如[NSPredicate predicateWithFormat:@"SELF CONTAINS[cd] 'CAfé'"]與[NSPredicate predicateWithFormat:@"SELF CONTAINS 'cafe'"]功能相同LIKE后面可以使用通配符?和银觅,但不能在%@占位符的值中使用
?表示指代一個(gè)字符 表示指代0個(gè)或多個(gè)字符
例如[NSPredicate predicateWithFormat:@"SELF LIKE '?world'"]表示aworldaaa這種字符串
[NSPredicate predicateWithFormat:@"SELF LIKE %@", @"?world"],這種是錯(cuò)誤寫(xiě)法坏为,只能識(shí)別字符串?world究驴,?在此處不能當(dāng)成通配符
集合操作
ANY镊绪、SOME 是否對(duì)象的元素至少有一個(gè)滿(mǎn)足后面的表達(dá)式
ALL 是否對(duì)象的元素全都滿(mǎn)足后面的表達(dá)式
NONE 是否對(duì)象的元素全都不滿(mǎn)足后面的表達(dá)式
上面的三種,評(píng)估的對(duì)象(evaluateWithObject的參數(shù))必須是NSArray或NSSet例如[NSPredicate predicateWithFormat:@"ANY SELF CONTAINS[cd] 'cafe'"]
表示@[@"cafe1", @"c afe2", @"c afe3"]這種數(shù)組
[NSPredicate predicateWithFormat:@"ALL SELF CONTAINS[cd] 'cafe'"]
表示@[@"cafe1", @"cafe2", @"cafe3"]這種數(shù)組
[NSPredicate predicateWithFormat:@"NONE SELF CONTAINS[cd] 'cafe'"]
表示@[@"c afe1", @"c afe2", @"c afe3"]這種數(shù)組IN 左邊元素是否在右邊元素集合出現(xiàn)洒忧,集合可以是NSArray蝴韭、NSSet、NSDictionary熙侍,如果是NSDictionary榄鉴,將取其所有value的值的數(shù)組。
例: //cafe是否在集合中 predicate = [NSPredicate predicateWithFormat:@"SELF IN[cd] {'cafe', 'apple', 'tea'}"]; eva = [predicate evaluateWithObject:@"cafe"]; NSLog(@"%@", @(eva)); 輸出:1
當(dāng)evaluateWithObject的參數(shù)為NSArray蛉抓、NSSet庆尘、NSDictionary時(shí),可使用以下寫(xiě)法表示單個(gè)元素芝雪。
SELF[index],表示取對(duì)應(yīng)索引的元素综苔,index不要越界
SELF[FIRST]惩系,表示第一個(gè)元素
SELF[LAST],表示最后一個(gè)元素
SELF[SIZE]如筛,表示元素的個(gè)數(shù)//數(shù)組的第0個(gè)元素是否在集合中 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),表示對(duì)應(yīng)key的元素//字典key為key1的值是否在集合中 predicate = [NSPredicate predicateWithFormat:@"SELF.key1 IN[cd] {'cafe', 'apple', 'tea'}"]; eva = [predicate evaluateWithObject:@{@"key1":@"apple"}]; NSLog(@"%@", @(eva)); 輸出:1
字面量語(yǔ)義
FALSE杨刨、NO 邏輯假
TRUE晤柄、YES 邏輯真predicate = [NSPredicate predicateWithFormat:@"SELF = TRUE"]; eva = [predicate evaluateWithObject:@(YES)];
五、應(yīng)用
//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時(shí)將上個(gè)model添加成subModel
model.subModel = [models lastObject];
}
[models addObject:model];
}
NSLog(@"models = %@", models);
//查詢(xún)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);
//二級(jí)查詢(xún) 查詢(xún)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))"
)