先來一段代碼,之前項目中用到,涉及到了NSPredicate和正則表達
//判斷手機號是否正確
+(BOOL) isValidateMobile:(NSString *)mobile
{
//手機號以13, 15,18 16 17開頭,八個 \d 數字字符
NSString *phoneRegex = @"^((13[0-9])|(15[^4,\\D])|(16[0-9])|(17[0-9])|(18[0,0-9]))\\d{8}$";
NSPredicate *phoneTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",phoneRegex];
return [phoneTest evaluateWithObject:mobile];
}
這是用來判斷手機號碼的真假,再來段判斷銀行卡號的:
//判斷16-19位的銀行卡號
+ (BOOL)judgeBankCardNumber:(NSString*)str
{
//16到19位數以6開頭的銀聯卡判斷
// NSString * pattern = @"";
// NSRegularExpression * reglx = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:nil];
// NSArray * array = [reglx]
NSString * cardNumRegex = @"^[6]\\d{3}\\s*\\d{4}\\s*\\d{4}\\s*\\d{4}$|^[6]\\d{3}\\s*\\d{4}\\s*\\d{4}\\s*\\d{4}\\s*\\d{1,3}$";
NSPredicate *numTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",cardNumRegex];
return [numTest evaluateWithObject:str];
}
我們會發現,通過使用了NSPredicate,代碼極大的簡化了。下面來分析下,什么是NSPredicate。
iOS Predicate 即謂詞邏輯。和數據庫的SQL語句具有相似性,都是從數據堆中根據條件進行篩選。
它的使用主要集中在兩個方法中:
NSArray
- (NSArray *)filteredArrayUsingPredicate:(NSPredicate *)predicate;
NSMutableArray
- (void)filterUsingPredicate:(NSPredicate *)predicate;
還有NSSet和NSMutableSet也可以用這個類篩選。
篩選用法
利用成員實例方法篩選出長度大于3的字符串
NSArray *array = @[@"jim", @"cook", @"jobs", @"sdevm"];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"length > 3"];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
打印
( cook, jobs, sdevm)
lenght
就是對數組成員執行[xxxx lenght]然后判斷返回的NSUInteger值是否大于3。擴展到NSString其他方法比如integerValue
NSArray *array = @[@"2", @"3", @"4", @"5"];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"integerValue >= %@", @3];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
如果我不想用任何實例方法,想篩選成員本身應該怎么做呢。這時候就可以用self來代替
NSPredicate *pre = [NSPredicate predicateWithFormat:@"self CONTAINS %@", @3];
CONTAINS用法后面會講到
再擴展到模型
Test.h
@interface Test : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSNumber *code;
@end
Test *test1 = [[Test alloc]init];
test1.name = @"西湖";
test1.code = @1;
Test *test2 = [[Test alloc]init];
test2.name = @"西溪濕地";
test2.code = @2;
Test *test3 = [[Test alloc]init];
test3.name = @"靈隱寺";
test3.code = @3;
NSArray *array = @[test1, test2, test3];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"code >= %@", @2];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
篩選出數組成員[test code]方法(code屬性的get方法)返回值>=2的成員。這里的比較運算符同樣也可以使用==、!=、<=、<。
其實==不僅可以用來比較NSNumber對象,還可以用來判斷NSString對象是否相同。
NSPredicate *pre = [NSPredicate predicateWithFormat:@"name == %@", @"西湖"];
篩選出name是"西湖"的對象數組。
NSString對象的操作
前面提到==比較運算符可以起到- (BOOL)isEqualToString:(NSString *)aString;方法的效果,來判斷字符串是否相同。那么字符串中包含某個字符串應該如何判斷呢,在NSPredicate中可以用CONTAINS(大小寫都可以)來表示包含關系。
NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS %@", @"湖"];
當判斷的時候需要忽略大小寫可以使用[cd]
[c] 忽略大小寫
[d] 忽略重音符號
[cd]既不區分大小寫,也不區分發音符號。
使用:
NSPredicate *pre = [NSPredicate predicateWithFormat:@"name CONTAINS[cd] %@", @"abc"];
再涉及到一些更復雜的查詢語句,比如判斷字符串以某個字符串開頭或者結尾,通配符的使用。
BEGINSWITH(已某個字符串開頭, begins with)
NSString *targetString = @"h"; NSPredicate *pre = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@",targetString];
ENDSWITH(已某個字符串結尾, ends with)
NSString *targetString = @"ing"; NSPredicate *pre = [NSPredicate predicateWithFormat:@"name ENDSWITH %@",targetString];
通配符 LIKE
*代表一個或者多個或者是空
?代表一個字符
Test *test1 = [[Test alloc]init];
test1.name = @"absr";
test1.code = @1;
Test *test2 = [[Test alloc]init];
test2.name = @"asb";
test2.code = @2;
Test *test3 = [[Test alloc]init];
test3.name = @"raskj";
test3.code = @3;
NSArray *array = @[test1, test2, test3];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"name LIKE %@", @"?b*"];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
結果是只有test1符合,like也可以接受[cd]
NSPredicate *pre = [NSPredicate predicateWithFormat:@"name LIKE[cd] %@", @"?b*"];
關系運算,包括了IN、BETWEEN、AND、OR、NOT
IN(之中)
NSPredicate *pre = [NSPredicate predicateWithFormat:@"code IN %@", @[@1, @3]];
判斷code是否@1或者是@2,也就是是否在數組中。
OR(或,可以用||代替)
OR可以用來代替IN達到同樣的效果,但是OR更靈活。
NSPredicate *pre = [NSPredicate predicateWithFormat:@"code == %@ OR code == %@ ", @1, @3];
效果和IN一樣,但是OR可以判斷不只一個屬性
NSPredicate *pred = [NSPredicate predicateWithFormat:@"code == %@ OR name == %@ ", @1, @"asb"];
BETWEEN(之間)
通常用于判斷NSNumber對象
NSPredicate *pred = [NSPredicate predicateWithFormat:@"code BETWEEN {1, 3}"];
判斷code是否>=1且<=3
AND(且,可以用&&代替)
NSPredicate *pred = [NSPredicate predicateWithFormat:@"code >= %@ AND code <=%@", @1, @3];
NOT(非,可以用!代替)
NOT
最常見的用法就是從一個數組中剔除另外一個數組的數據,可能有點繞,舉個例子就很明朗了。
NSArray *arrayFilter = @[@"abc1", @"abc2"];
NSArray *arrayContent = @[@"a1", @"abc1", @"abc4", @"abc2"];
NSPredicate *thePredicate = [NSPredicate predicateWithFormat:@"NOT (SELF in %@)", arrayFilter];
NSLog(@"%@",[arrayContent filteredArrayUsingPredicate:thePredicate]);
打印
( a1, abc4)
比起循環比較再加到新數組中,簡單的不止一兩點。
前面提到的都是用+ (NSPredicate *)predicateWithFormat:(NSString )predicateFormat, ...;方法創建,還有另一種常用的方法:+ (NSPredicate)predicateWithBlock:(BOOL (^)(id evaluatedObject, NSDictionary *bindings))block,用Block形式創建
NSPredicate *pre = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
Test *test = (Test *)evaluatedObject;
if (test.code.integerValue > 2) {
return YES;
}
else{
return NO;
}
}];
參數evaluatedObject表示數組成員,block必須返回YES或者NO,分別表示匹配還是不匹配。請忽略bindings參數,具體作用我也沒搞清楚。
多重篩選
如果需要匹配數個屬性的篩選,用AND或者OR來串聯顯然有點麻煩,NSCompoundPredicate類可以滿足我們的需求,它可以將多個NSPredicate對象的組合,組合方式可以是AND或者OR。
NSPredicate *pre1 = [NSPredicate predicateWithFormat:@"code >= %@", @3];
NSPredicate *pre2 = [NSPredicate predicateWithFormat:@"code <= %@", @2];
//以AND形式組合
NSPredicate *pre = [NSCompoundPredicate andPredicateWithSubpredicates:@[pre1,pre2]];
//以OR形式組合
NSPredicate *pre = [NSCompoundPredicate orPredicateWithSubpredicates:@[pre1, pre2]];
匹配用法
其實NSPredicate不僅可以用于篩選,還可以用來判斷匹配直接返回是否符合,主要方法是- (BOOL)evaluateWithObject:(id)object;,用法:
Test *test1 = [[Test alloc]init];
test1.name = @"absr";
test1.code = @1;
NSPredicate *pres = [NSPredicate predicateWithFormat:@"code == %@", @2];
BOOL match = [pres evaluateWithObject:test1];
當然最常用的還是配合配合正則表達式,列舉幾個常用的正則:
就像開頭提到的2個用法,就是一個例子
是否以a開頭以e結尾
NSString *string=@"assdbfe";
NSString *targetString=@"^a.+e$";
NSPredicate *pres = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", targetString];
BOOL match = [pres evaluateWithObject:string];
是否是郵箱
NSString *strRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{1,5}";
是否是手機號
NSString *strRegex = @"[0-9]{1,20}";
坑
不得不說,在利用+ (NSPredicate *)predicateWithFormat:(NSString *)predicateFormat, ...;方法創建的NSPredicate對象的時候有個坑。
在某些情況下,類似于上面例子中的code字符串不是很明確,創建的時候就會這樣使用
Test *test1 = [[Test alloc]init];
test1.name = @"absr";
test1.code = @1;
Test *test2 = [[Test alloc]init];
test2.name = @"asb";
test2.code = @2;
Test *test3 = [[Test alloc]init];
test3.name = @"raskj";
test3.code = @3;
NSArray *array = @[test1, test2, test3];
NSPredicate *pre = [NSPredicate predicateWithFormat:@"%@ == %@", @"code", @2];
NSLog(@"%@", [array filteredArrayUsingPredicate:pre]);
注意NSPredicate對象的初始化方式。運行這塊代碼你會發現test2對象沒有被查詢出來。打印pre發現"code" == 2,這說明查找的是"code"方法的返回值,這顯然行不通。
如果查詢的是屬性,比如code是Test類的屬性,那么利用下面的創建方式
NSPredicate *pre = [NSPredicate predicateWithFormat:@"%K == %@", @"code", @2];
就不會被坑
注意:%K的K必須是大寫。
最后
NSPredicate幾乎可以滿足所有形式的查詢,配合Core Data的數據庫查詢當然不在話下。NSPredicate用法不只這些,有興趣的同學可以看下nshipster網站的這篇博文,里面提到了一些我沒有涉及到的用法。