首先骑疆,我們需要知道何謂謂詞,讓我們看看官方的解釋:The NSPredicate class is used to define logical conditions used to constrain a search either for a fetch or for in-memory filtering.NSPredicate類是用來定義邏輯條件約束的獲取或內(nèi)存中的過濾搜索禀崖。可以使用謂詞來表示邏輯條件螟炫,用于描述對象持久性存儲在內(nèi)存中的對象過濾波附。其實意思就是:我是一個過濾器,不符合條件的都滾開昼钻。一掸屡、NSPredicate的基本語法我們使用一門語言,無論是外語還是計算機語言然评,總是從語法開始的仅财,這樣我們才能正確的把握邏輯。所以我們從語法開始說起碗淌。在這部分我們僅關心其語法的使用只要我們使用謂詞(NSPredicate)都需要為謂詞定義謂詞表達式,而這個表達式必須是一個返回BOOL的值盏求。
謂詞表達式由表達式、運算符和值構(gòu)成贯莺。
1.比較運算符如下
=风喇、==:判斷兩個表達式是否相等,在謂詞中=和==是相同的意思都是判斷缕探,而沒有賦值這一說
NSNumber *testNumber = @123;?
?NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF = 123"];??
if ([predicate evaluateWithObject:testNumber]) { ? ?
? NSLog(@"testString:%@", testNumber);?
?}
我們可以看到輸出的內(nèi)容為:
2016-01-07 11:12:27.281 PredicteDemo[4130:80412] testString:123>=魂莫,=>:判斷左邊表達式的值是否大于或等于右邊表達式的值<=,=<:判斷右邊表達式的值是否小于或等于右邊表達式的值>:判斷左邊表達式的值是否大于右邊表達式的值<:判斷左邊表達式的值是否小于右邊表達式的值!=爹耗、<>:判斷兩個表達式是否不相等BETWEEN:BETWEEN表達式必須滿足表達式 BETWEEN {下限耙考,上限}的格式谜喊,要求該表達式必須大于或等于下限,并小于或等于上限
NSNumber *testNumber = @123;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF BETWEEN {100, 200}"];?
?if ([predicate evaluateWithObject:testNumber]) {? ? ?
?NSLog(@"testString:%@", testNumber);??
} else {? ?
?? NSLog(@"不符合條件");
? }
輸出結(jié)果為:2016-01-07 11:20:39.921 PredicteDemo[4366:85408] testString:1232.
邏輯運算符AND倦始、&&:邏輯與斗遏,要求兩個表達式的值都為YES時,結(jié)果才為YES鞋邑。NSArray *testArray = @[@1, @2, @3, @4, @5, @6];??
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF > 2 && SELF < 5"];?
?NSArray *filterArray = [testArray filteredArrayUsingPredicate:predicate];??
NSLog(@"filterArray:%@", filterArray);
輸出結(jié)果為:2016-01-07 11:27:01.885 PredicteDemo[4531:89537] filterArray:(? 3,? 4)OR诵次、||:邏輯或,要求其中一個表達式為YES時枚碗,結(jié)果就是YES
NOT逾一、 !:邏輯非,對原有的表達式取反3.字符串比較運算符BEGINSWITH:檢查某個字符串是否以指定的字符串開頭(如判斷字符串是否以a開頭:BEGINSWITH 'a')ENDSWITH:檢查某個字符串是否以指定的字符串結(jié)尾CONTAINS:檢查某個字符串是否包含指定的字符串LIKE:檢查某個字符串是否匹配指定的字符串模板肮雨。其之后可以跟?代表一個字符和*代表任意多個字符兩個通配符遵堵。比如"name LIKE '*ac*'",這表示name的值中包含ac則返回YES怨规;"name LIKE '?ac*'"陌宿,表示name的第2、3個字符為ac時返回YES波丰。MATCHES:檢查某個字符串是否匹配指定的正則表達式壳坪。雖然正則表達式的執(zhí)行效率是最低的,但其功能是最強大的呀舔,也是我們最常用的弥虐。注:字符串比較都是區(qū)分大小寫和重音符號的。如:café和cafe是不一樣的媚赖,Cafe和cafe也是不一樣的霜瘪。如果希望字符串比較運算不區(qū)分大小寫和重音符號,請在這些運算符后使用[c]惧磺,[d]選項颖对。其中[c]是不區(qū)分大小寫,[d]是不區(qū)分重音符號磨隘,其寫在字符串比較運算符之后缤底,比如:name LIKE[cd] 'cafe',那么不論name是cafe番捂、Cafe還是café上面的表達式都會返回YES个唧。4.集合運算符ANY、SOME:集合中任意一個元素滿足條件设预,就返回YES徙歼。ALL:集合中所有元素都滿足條件,才返回YES。NONE:集合中沒有任何元素滿足條件就返回YES魄梯。如:NONE person.age < 18桨螺,表示person集合中所有元素的age>=18時,才返回YES酿秸。IN:等價于SQL語句中的IN運算符灭翔,只有當左邊表達式或值出現(xiàn)在右邊的集合中才會返回YES。我們通過一個例子來看一下
NSArray *filterArray = @[@"ab", @"abc"];??
NSArray *array = @[@"a", @"ab", @"abc", @"abcd"];?
?NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", filterArray];? NSLog(@"%@", [array filteredArrayUsingPredicate:predicate]);
代碼的作用是將array中和filterArray中相同的元素去除辣苏,輸出為:2016-01-07 13:17:43.669 PredicteDemo[6701:136206] (? a,? abcd)array[index]:返回array數(shù)組中index索引處的元素array[FIRST]:返回array數(shù)組中第一個元素array[LAST]:返回array數(shù)組中最后一個元素array[SIZE]:返回array數(shù)組中元素的個數(shù)5.直接量在謂詞表達式中可以使用如下直接量FALSE肝箱、NO:代表邏輯假TRUE、YES:代表邏輯真NULL稀蟋、NIL:代表空值SELF:代表正在被判斷的對象自身"string"或'string':代表字符串數(shù)組:和c中的寫法相同狭园,如:{'one', 'two', 'three'}。數(shù)值:包括證書糊治、小數(shù)和科學計數(shù)法表示的形式十六進制數(shù):0x開頭的數(shù)字八進制:0o開頭的數(shù)字二進制:0b開頭的數(shù)字6.保留字下列單詞都是保留字(不論大小寫)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逊彭、CAST、TRUEPREDICATE构订、FALSEPREDICATE注:雖然大小寫都可以侮叮,但是更推薦使用大寫來表示這些保留字二、謂詞的用法1.定義謂詞一般我們使用下列方法來定義一個謂詞NSPredicate *predicate = [NSPredicate predicateWithFormat:<#(nonnull NSString *), ...#>];下面我們通過幾個簡單的例子來看看它該如何使用:首先我們需要定義一個模型悼瘾,因為示例中需要用到它ZLPersonModel.h#importtypedef NS_ENUM(NSInteger, ZLPersonSex) {? ?
?ZLPersonSexMale = 0,? ? ZLPersonSexFamale};@interface ZLPersonModel : NSObject/** NSString 姓名 */@property (nonatomic, copy) ?NSString *name;/** NSUInteger 年齡 */@property (nonatomic, assign, readonly) NSUInteger age;/** ZLPersonSex 性別 */@property (nonatomic, assign, readonly) ZLPersonSex sex;
+ (instancetype)personWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex;@endZLPersonModel.m#import "ZLPersonModel.h"@implementation ZLPersonModel
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex{ ??
if (self = [super init]) { ? ??
?? _name = name;? ? ? ??
? ?_age = age;? ? ? ??
? ?_sex = sex;? ?
?}? ?
?return self;
}
+ (instancetype)personWithName:(NSString *)name age:(NSUInteger)age sex:(ZLPersonSex)sex{? ?
?return ?[[self alloc] initWithName:name age:age sex:sex];
}
- (NSString *)description{? ?
?return ?[NSString stringWithFormat:@"[name = %@, age = %ld, sex = %ld]", self.name, self.age, self.sex];
}
@end
下面讓我們進入正題例一:(最簡單的使用)
ZLPersonModel *sunnyzl = [ZLPersonModel personWithName:@"sunnyzl" age:29 sex:ZLPersonSexMale];?
?? ZLPersonModel *jack = ?[ZLPersonModel personWithName:@"jack" age:22 sex:ZLPersonSexMale];? ?
?//? 首先我們來看一些簡單的使用? ??
//? 1.判斷姓名是否是以s開頭的??
? NSPredicate *pred1 = [NSPredicate predicateWithFormat:@"name LIKE 's*'"];? ? //? 輸出為:sunnyzl:1, jack:0? ? NSLog(@"sunnyzl:%d, jack:%d", [pred1 evaluateWithObject:sunnyzl], [pred1 evaluateWithObject:jack]);? ?
?//? 2.判斷年齡是否大于25??
? NSPredicate *pred2 = [NSPredicate predicateWithFormat:@"age > 25"];? ?
?//? 輸出為:sunnyzl的年齡是否大于25:1, jack的年齡是否大于25:0? ?
?NSLog(@"sunnyzl的年齡是否大于25:%d, jack的年齡是否大于25:%d", [pred2 evaluateWithObject:sunnyzl], [pred2 evaluateWithObject:jack]);
看到這里我們會發(fā)現(xiàn)evaluateWithObject:方法返回的是一個BOOL值囊榜,如果符合條件就返回YES,不符合就返回NO亥宿。而即使是最簡單的使用也有一些大用處卸勺,比如以前我們寫判斷手機號碼、郵編等等烫扼,像我就喜歡用John Engelhart大神的RegexKitLite曙求,然而由于年代久遠需要導入libicucore.dylib庫(xcode7為libicucore.tbd)且由于是mrc又需要添加-fno-objc-arc,至此我們才能使用映企。然而使用謂詞讓我們可以用同樣簡潔的代碼實現(xiàn)相同的功能例二:
(判斷手機號是否正確)?
- (BOOL)checkPhoneNumber:(NSString *)phoneNumber{??
? NSString *regex = @"^[1][3-8]\\d{9}$";? ??
?NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];??
? return [pred evaluateWithObject:phoneNumber];
}
看到這里是不是感覺好爽悟狱,感覺以前所有的正則都可以這么匹配,但是謂詞匹配正則時也是有缺點的堰氓,下面通過一個例子來看一下這個致命的缺點例三:謂詞匹配正則的缺點(本意:檢測字符串中是否有特殊字符)
- (BOOL)checkSpecialCharacter:(NSString *)string{??
? NSString *regex = @"[`~!@#$^&*()=|{}':;',\\[\\].<>/?~挤渐!@#¥……&*()——|{}【】‘;:”“'双絮。浴麻,、囤攀?]";? ? NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];? ?
?return [pred evaluateWithObject:string];
}
我們想要的效果是字符串中有特殊字符時就返回YES软免,然而夢想是美好的,現(xiàn)實是殘酷的讓我們看看這悲催的結(jié)局
NSString *testString = @"!";NSLog(@"是否含有特殊字符:%d", [self checkSpecialCharacter:testString]);
//? 當testString為一個特殊字符時抚岗,我們驚喜的發(fā)現(xiàn)輸出為//? 是否含有特殊字符:
1看到這里我們心里猛然一喜或杠,這tmd根本沒問題嘛讓我們修改下testString的值
NSString *testString = @"!~";NSLog(@"%d", [self checkSpecialCharacter:testString]);
//? 我們會發(fā)現(xiàn)悲催的結(jié)局來了輸出為
//? 是否含有特殊字符:0再次修改testString的值NSString *testString = @"abc!~d";NSLog(@"%d", [self checkSpecialCharacter:testString]);//? 我們會發(fā)現(xiàn)輸出為
//? 是否含有特殊字符:0這總與我們的想法事與愿違,看到這里我們會發(fā)現(xiàn)謂詞對正則并不像我們使用NSRegularExpression時匹配的那么好宣蔚,究其原因是為什么呢向抢?我們用NSRegularExpression時會發(fā)現(xiàn)匹配到一個結(jié)果時就會存入數(shù)組,再從匹配到的位置繼續(xù)向下匹配胚委。然而NSPredicate并不會做這樣的自動操作挟鸠,我們最終發(fā)現(xiàn)在NSPredicate輸入[`~!@#$^&*()=|{}':;',\[\].<>/?~!@#¥……&*()——|{}【】‘亩冬;:”“'艘希。硼身,、覆享?]正則表達式時和寫成^[`~!@#$^&*()=|{}':;',\[\].<>/?~佳遂!@#¥……&*()——|{}【】‘;:”“'撒顿。丑罪,、凤壁?]$的效果是一樣的吩屹。所以通過這個例子我們總結(jié)出來,只有在正則表達式為^表達式$時才使用謂詞拧抖,而不是所有情況都使用煤搜。當然上例中我們可以用一個投機取巧的方法實現(xiàn)(但是僅能用于匹配是否包含特殊符號,而無法像NSRegularExpression那樣對這些特殊符號進行復雜操作)我們可以將
- (BOOL)checkSpecialCharacter:(NSString *)string更改為:- (BOOL)checkSpecialCharacter:(NSString *)string{? ?
?NSString *regex = @".*[`~!@#$^&*()=|{}':;',\\[\\].<>/?~唧席!@#¥……&*()——|{}【】‘擦盾;:”“'。淌哟,厌衙、?].*";? ?
?NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];? ? return [pred evaluateWithObject:string];
}
其實上述方法也只是匹配了一次绞绒,只不過我們將它的范圍擴大了。那么我們是不是因為這一點就摒棄它了呢榕暇,答案是否定的蓬衡。因為雖然NSPredicate有這么一點瑕疵,但是它總體帶給我們的便利其實除了正則表達式匹配時的這個問題外是更多的彤枢。2.使用謂詞過濾集合此部分是我們需要掌握的重點狰晚,因為從這里我們就可以看到謂詞的真正的強大之處其實謂詞本身就代表了一個邏輯條件,計算謂詞之后返回的結(jié)果永遠為BOOL類型的值缴啡。而謂詞最常用的功能就是對集合進行過濾壁晒。當程序使用謂詞對集合元素進行過濾時,程序會自動遍歷其元素业栅,并根據(jù)集合元素來計算謂詞的值秒咐,當這個集合中的元素計算謂詞并返回YES時,這個元素才會被保留下來碘裕。請注意程序會自動遍歷其元素携取,它會將自動遍歷過之后返回為YES的值重新組合成一個集合返回。其實類似于我們使用tableView設置索引時使用的下段代碼
- (NSArray*)sectionIndexTitlesForTableView:(UITableView *)tableView{? ? return [self.cityGroup valueForKey:@"title"];}中的[self.cityGroup valueForKey:@"title"]帮孔。
它的作用是遍歷所有title并將得到的值組成新的數(shù)組雷滋。NSArray提供了如下方法使用謂詞來過濾集合
- (NSArray*)filteredArrayUsingPredicate:(NSPredicate *)predicate:使用指定的謂詞過濾NSArray集合,返回符合條件的元素組成的新集合NSMutableArray提供了如下方法使用謂詞來過濾集合、
- (void)filterUsingPredicate:(NSPredicate *)predicate:使用指定的謂詞過濾NSMutableArray晤斩,剔除集合中不符合條件的元素NSSet提供了如下方法使用謂詞來過濾集合
- (NSSet*)filteredSetUsingPredicate:(NSPredicate *)predicate NS_AVAILABLE(10_5, 3_0):作用同NSArray中的方法
NSMutableSet提供了如下方法使用謂詞來過濾集合
- (void)filterUsingPredicate:(NSPredicate *)predicate NS_AVAILABLE(10_5, 3_0):作用同NSMutableArray中的方法焕檬。
通過上面的描述可以看出,使用謂詞過濾不可變集合和可變集合的區(qū)別是:過濾不可變集合時澳泵,會返回符合條件的集合元素組成的新集合实愚;過濾可變集合時,沒有返回值烹俗,會直接剔除不符合條件的集合元素
下面讓我們來看幾個例子:
例一:
NSMutableArray *arrayM = [@[@20, @40, @50, @30, @60, @70] mutableCopy];
//? 過濾大于50的值
NSPredicate *pred1 = [NSPredicate predicateWithFormat:@"SELF > 50"];
[arrayM filterUsingPredicate:pred1];
NSLog(@"arrayM:%@", arrayM);
NSArray *array = @[[ZLPersonModel personWithName:@"Jack" age:20 sex:ZLPersonSexMale],
[ZLPersonModel personWithName:@"Rose" age:22 sex:ZLPersonSexFamale],
[ZLPersonModel personWithName:@"Jackson" age:30 sex:ZLPersonSexMale],
[ZLPersonModel personWithName:@"Johnson" age:35 sex:ZLPersonSexMale]];
//? 要求取出包含‘son’的元素
NSPredicate *pred2 = [NSPredicate predicateWithFormat:@"name CONTAINS 'son'"];
NSArray *newArray = [array filteredArrayUsingPredicate:pred2];
NSLog(@"%@", newArray);
輸出為
2016-01-07 16:50:09.510 PredicteDemo[13660:293822] arrayM:(
60,
70
)
2016-01-07 16:50:09.511 PredicteDemo[13660:293822] (
"[name = Jackson, age = 30, sex = 0]",
"[name = Johnson, age = 35, sex = 0]"
)
從這個例子我們就可以看到NSPredicate有多么強大爆侣,如果讓我們用其他的方法來實現(xiàn)又是一大堆if...else。
讓我們來回顧一下上面的從第二個數(shù)組中去除第一個數(shù)組中相同的元素
例二:
NSArray *filterArray = @[@"ab", @"abc"];
NSArray *array = @[@"a", @"ab", @"abc", @"abcd"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT (SELF IN %@)", filterArray];
NSLog(@"%@", [array filteredArrayUsingPredicate:predicate]);
輸出為:
2016-01-07 13:17:43.669 PredicteDemo[6701:136206] (
a,
abcd
)
如果我們不用NSPredicate的話幢妄,肯定又是各種if...else兔仰,for循環(huán)等等〗对В可以看出NSPredicate的出現(xiàn)為我們節(jié)省了大量的時間和精力乎赴。
3.在謂詞中使用占位符參數(shù)
我們上面所有的例子中謂詞總是固定的,然而我們在現(xiàn)實中處理變量時決定了謂詞應該是可變的潮尝。下面我們來看看如果讓謂詞變化起來榕吼。
首先如果我們想在謂詞表達式中使用變量,那么我們需要了解下列兩種占位符
%K:用于動態(tài)傳入屬性名
%@:用于動態(tài)設置屬性值
其實相當于變量名與變量值
除此之外勉失,還可以在謂詞表達式中使用動態(tài)改變的屬性值羹蚣,就像環(huán)境變量一樣
NSPredicate *pred = [NSPredicate predicateWithFormat:@"SELF CONTAINS $VALUE"];
上述表達式中,$VALUE是一個可以動態(tài)變化的值乱凿,它其實最后是在字典中的一個key顽素,所以可以根據(jù)你的需要寫不同的值,但是必須有$開頭徒蟆,隨著程序改變$VALUE這個謂詞表達式的比較條件就可以動態(tài)改變胁出。
下面我們通過一個例子來看看這三個重要的占位符應該如何使用
例一:
NSArray *array = @[[ZLPersonModel personWithName:@"Jack" age:20 sex:ZLPersonSexMale],
[ZLPersonModel personWithName:@"Rose" age:22 sex:ZLPersonSexFamale],
[ZLPersonModel personWithName:@"Jackson" age:30 sex:ZLPersonSexMale],
[ZLPersonModel personWithName:@"Johnson" age:35 sex:ZLPersonSexMale]];
//? 定義一個property來存放屬性名,定義一個value來存放值
NSString *property = @"name";
NSString *value = @"Jack";
//? 該謂詞的作用是如果元素中property屬性含有值value時就取出放入新的數(shù)組內(nèi)段审,這里是name包含Jack
NSPredicate *pred = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", property, value];
NSArray *newArray = [array filteredArrayUsingPredicate:pred];
NSLog(@"newArray:%@", newArray);
//? 創(chuàng)建謂詞全蝶,屬性名改為age,要求這個age包含$VALUE字符串
NSPredicate *predTemp = [NSPredicate predicateWithFormat:@"%K > $VALUE", @"age"];
// 指定$VALUE的值為 25
NSPredicate *pred1 = [predTemp predicateWithSubstitutionVariables:@{@"VALUE" : @25}];
NSArray *newArray1 = [array filteredArrayUsingPredicate:pred1];
NSLog(@"newArray1:%@", newArray1);
//? 修改 $VALUE的值為32
NSPredicate *pred2 = [predTemp predicateWithSubstitutionVariables:@{@"VALUE" : @32}];
NSArray *newArray2 = [array filteredArrayUsingPredicate:pred2];
NSLog(@"newArray2:%@", newArray2);
輸出為
2016-01-07 17:28:02.062 PredicteDemo[14542:309494] newArray:(
"[name = Jack, age = 20, sex = 0]",
"[name = Jackson, age = 30, sex = 0]"
)
2016-01-07 17:28:02.063 PredicteDemo[14542:309494] newArray1:(
"[name = Jackson, age = 30, sex = 0]",
"[name = Johnson, age = 35, sex = 0]"
)
2016-01-07 17:28:02.063 PredicteDemo[14542:309494] newArray2:(
"[name = Johnson, age = 35, sex = 0]"
)