Cocoa中謂詞(Predicate)提供了一個通用的查詢方式處理數據,可以獲取和指定數據的過濾形式,Cocoa實際開發中可以是使用NSPredicate及其父類NSComparisonPredicate和NSCompoundPredicate.其風格類似於SQL查詢語言和正則表達式的混合體,提供了具有表現力的,自然語言界面來定義一個集合被搜尋的邏輯條件。一般來說稍微操作過數據庫基本上很容易理解其中的方法,至於使用的方法也很簡單。
集合中的NSPredicate
首先來一段比較簡單的代碼:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF IN %@", @[@"keso", @"FlyElephant", @"博客園"]];
if ([predicate evaluateWithObject:@"keso"]) {
NSLog(@"keso");
};
第一行代碼初始化一個查詢條件,第二句就是判斷數據在不在結果集中,跟SQL基本上一樣,通過IN就能大概了解其功能,SELF表示本身,非常常用。
接下來可以類似於SQL中like語句的代碼:
NSArray *array = [[NSArray alloc]initWithObjects:@"北京",@"上海",@"廣州",@"深圳",nil];
NSPredicate *preBegin= [NSPredicate predicateWithFormat:@"SELF beginswith[c] %@",@"北"];
NSPredicate *preContain= [NSPredicate predicateWithFormat:@"SELF contains[c] %@",@"海"];
NSLog(@"%@",[array filteredArrayUsingPredicate:preBegin][0]);
NSLog(@"%@",[array filteredArrayUsingPredicate:preContain][0]);
第一次就是先初始化數組,然后創建條件,通過filteredArrayUsingPredicate過濾數組;
NSDictionary可以用謂詞來過濾它的鍵和值(兩者都為NSArray對象);
NSOrderedSet可以由過濾的NSArray或NSSet生成一個新的有序的集,或者NSMutableSet可以簡單的removeObjectsInArray來傳遞通過否定predicate過濾的對象。
Core Data中的NSPredicate
謂詞的在Core Data同樣適用,在管理對象環境中,謂詞由持久化存儲助理(persistent store coordinator)評估,而集合則是內存中過濾。這時候我們有必要里了解一下基本比較運算符:
- =, ==:左邊的表達式和右邊的表達式相等。
- >=, =>:左邊的表達式大於或者等於右邊的表達式。
- <=, =<:左邊的表達式小於等於右邊的表達式。
- >:左邊的表達式大於右邊的表達式。
- <:左邊的表達式小於右邊的表達式。
- !=, <>:左邊的表達式不等於右邊的表達式。
NSPredicate *betweenPredicate =
[NSPredicate predicateWithFormat: @"attributeName BETWEEN %@", @[@1, @10]];
NSDictionary *dictionary = @{ @"attributeName" : @5 };
BOOL between = [betweenPredicate evaluateWithObject:dictionary];
if (between) {
NSLog(@"比較運算符between");
}
- AND, &&:邏輯與.
- OR, ||:邏輯或.
- NOT, !:邏輯非
- BEGINSWITH[cd] $FIRST_NAME。
- BEGINSWITH:左邊的表達式以右邊的表達式作為開始。
- CONTAINS:左邊的表達式包含右邊的表達式。
- ENDSWITH:左邊的表達式以右邊的表達式作為結束。
- LIKE:左邊的表達式等於右邊的表達式:?和*可作為通配符,其中?匹配1個字符,*匹配0個或者多個字符。
- MATCHES:左邊的表達式根據ICU v3的regex風格比較,等於右邊的表達式。
NSString *regex = @"[A-Za-z]+";
NSPredicate *matchpredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
if ([matchpredicate evaluateWithObject:@"keso"]) {
NSLog(@"匹配成功");
}
- ANY,SOME:指定下列表達式中的任意元素。比如,ANY children.age < 18。
- ALL:指定下列表達式中的所有元素。比如,ALL children.age < 18。
- NONE:指定下列表達式中沒有的元素。比如,NONE children.age < 18。它在邏輯上等於NOT (ANY ...)。
- IN:等於SQL的IN操作,左邊的表達必須出現在右邊指定的集合中。比如,name IN { 'Ben', 'Melissa', 'Nick' }。
- array[index]:指定數組中特定索引處的元素。
- array[FIRST]:指定數組中的第一個元素。
- array[LAST]:指定數組中的最后一個元素。
- array[SIZE]:指定數組的大小。
- TRUEPREDICATE:結果始終為真的謂詞。
- FALSEPREDICATE:結果始終為假的謂詞。
@interface People : NSObject @property (strong,nonatomic) NSString *FirstName; @property (strong,nonatomic) NSString *LastName; @property (nonatomic) NSInteger Height; - (NSString *)description; @end
People.m文件:
@implementation People
- (NSString *)description {
return [NSString stringWithFormat:@"%@%@",self.LastName, self.FirstName];
};
@end
具體實現代碼:
NSArray *firstNames =[[NSArray alloc]initWithObjects:@"澤東", @"恩來", @"介石", @"中山", nil];
NSArray *lastNames = @[ @"毛", @"周", @"蔣", @"孫" ];
NSArray *familiar = @[ @100, @99, @99, @98 ];
NSMutableArray *dataList= [NSMutableArray array];
[firstNames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
People *people= [[People alloc] init];
people.FirstName = firstNames[idx];
people.LastName = lastNames[idx];
people.Height= [familiar[idx] integerValue];
[dataList addObject:people];
}];
NSPredicate *firstPredicate = [NSPredicate predicateWithFormat:@"FirstName = '恩來'"];
NSPredicate *lastPredicate = [NSPredicate predicateWithFormat:@"LastName = %@", @"蔣"];
NSPredicate *heightPredicate = [NSPredicate predicateWithFormat:@"Height < 99"];
//名搜索
NSLog(@"名: %@",[dataList filteredArrayUsingPredicate:firstPredicate][0]);
//姓搜索
NSLog(@"姓: %@", [dataList filteredArrayUsingPredicate:lastPredicate][0]);
//知名度
NSLog(@"知名度: %@", [dataList filteredArrayUsingPredicate:heightPredicate][0]);
其實開始講的NSCompoundPredicate和NSComparisonPredicate,因為有關系操作,基本上類似,如果通過混合搜索可以使用and,or實現,比如一下代碼是等價的:
NSCompoundPredicate *comPredicate=[NSCompoundPredicate andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"Height > 90"], [NSPredicate predicateWithFormat:@"FirstName = %@", @"介石"]]];
NSPredicate *secondPredicate=[NSPredicate predicateWithFormat:@"(Height > 90) AND (FirstName = %@)", @"介石"];
NSComparisonPredicate有兩個調用的靜態方法:
+ (NSComparisonPredicate *)predicateWithLeftExpression:(NSExpression *)lhs rightExpression:(NSExpression *)rhs modifier:(NSComparisonPredicateModifier)modifier type:(NSPredicateOperatorType)type options:(NSComparisonPredicateOptions)options; + (NSComparisonPredicate *)predicateWithLeftExpression:(NSExpression *)lhs rightExpression:(NSExpression *)rhs customSelector:(SEL)selector;
其實就是一個表達式的拼接的過程,不過具體的實現蘋果給封裝好了,一下是NSPredicateOperatorType類型:
typedef NS_ENUM(NSUInteger, NSPredicateOperatorType) {
NSLessThanPredicateOperatorType = 0, // compare: returns NSOrderedAscending
NSLessThanOrEqualToPredicateOperatorType, // compare: returns NSOrderedAscending || NSOrderedSame
NSGreaterThanPredicateOperatorType, // compare: returns NSOrderedDescending
NSGreaterThanOrEqualToPredicateOperatorType, // compare: returns NSOrderedDescending || NSOrderedSame
NSEqualToPredicateOperatorType, // isEqual: returns true
NSNotEqualToPredicateOperatorType, // isEqual: returns false
NSMatchesPredicateOperatorType,
NSLikePredicateOperatorType,
NSBeginsWithPredicateOperatorType,
NSEndsWithPredicateOperatorType,
NSInPredicateOperatorType, // rhs contains lhs returns true
NSCustomSelectorPredicateOperatorType,
NSContainsPredicateOperatorType NS_ENUM_AVAILABLE(10_5, 3_0) = 99, // lhs contains rhs returns true
NSBetweenPredicateOperatorType NS_ENUM_AVAILABLE(10_5, 3_0)
};
參考資料:https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Predicates/Articles/pSyntax.html#//apple_ref/doc/uid/TP40001795-SW1
