轉自 http://www.cocoachina.com/industry/20140321/8024.htmlhtml
NSPredicate是一個Foundation類,它指定數據被獲取或者過濾的方式。它的查詢語言就像SQL的WHERE和正則表達式的交叉同樣,提供了具備表現力的,天然語言界面來定義一個集合被搜尋的邏輯條件。正則表達式
索引 |
1 |
2 |
3 |
4 |
名 |
Alice |
Bob |
Charlie |
Quentin |
姓 |
Smith |
Jones |
Smith |
Alberts |
年齡 |
24 |
27 |
33 |
31 |
- @interface Person : NSObject
- @property NSString *firstName;
- @property NSString *lastName;
- @property NSNumber *age;
- @end
-
- @implementation Person
-
- - (NSString *)description {
- return [NSString stringWithFormat:@"%@ %@", self.firstName, self.lastName];
- }
-
- @end
-
- #pragma mark -
-
- NSArray *firstNames = @[ @"Alice", @"Bob", @"Charlie", @"Quentin" ];
- NSArray *lastNames = @[ @"Smith", @"Jones", @"Smith", @"Alberts" ];
- NSArray *ages = @[ @24, @27, @33, @31 ];
-
- NSMutableArray *people = [NSMutableArray array];
- [firstNames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- Person *person = [[Person alloc] init];
- person.firstName = firstNames[idx];
- person.lastName = lastNames[idx];
- person.age = ages[idx];
- [people addObject:person];
- }];
-
- NSPredicate *bobPredicate = [NSPredicate predicateWithFormat:@"firstName = 'Bob'"];
- NSPredicate *smithPredicate = [NSPredicate predicateWithFormat:@"lastName = %@", @"Smith"];
- NSPredicate *thirtiesPredicate = [NSPredicate predicateWithFormat:@"age >= 30"];
-
- NSLog(@"Bobs: %@", [people filteredArrayUsingPredicate:bobPredicate]);
-
- NSLog(@"Smiths: %@", [people filteredArrayUsingPredicate:smithPredicate]);
-
- NSLog(@"30's: %@", [people filteredArrayUsingPredicate:thirtiesPredicate]);
集合中使用NSPredicate
Foundation提供使用謂詞(predicate)來過濾NSArray/NSMutableArray&NSSet/NSMutableSet的方法。
不可變的集合,NSArray&NSSet,有能夠經過評估接收到的predicate來返回一個不可變集合的方法filteredArrayUsingPredicate:和filteredSetUsingPredicate:。
可變集合,NSMutableArray&NSMutableSet,可使用方法filterUsingPredicate:,它能夠經過運行接收到的謂詞來移除評估結果爲FALSE的對象。
NSDictionary能夠用謂詞來過濾它的鍵和值(二者都爲NSArray對象)。NSOrderedSet能夠由過濾的NSArray或 NSSet生成一個新的有序的集,或者NSMutableSet能夠簡單的removeObjectsInArray:,來傳遞經過否認 predicate過濾的對象。
Core Data中使用NSPredicate
NSFetchRequest有一個predicate屬性,它能夠指定管理對象應該被獲取的邏輯條件。謂詞的使用規則在這裏一樣適用,惟一的 區別在於,在管理對象環境中,謂詞由持久化存儲助理(persistent store coordinator)評估,而不像集合那樣在內存中被過濾。
謂詞語法
替換
%@是對值爲字符串,數字或者日期的對象的替換值。
%K是key path的替換值。
- NSPredicate *ageIs33Predicate = [NSPredicate predicateWithFormat:@"%K = %@", @"age", @33];
-
- NSLog(@"Age 33: %@", [people filteredArrayUsingPredicate:ageIs33Predicate]);
$VARIABLE_NAME是能夠被NSPredicate -predicateWithSubstitutionVariables:替換的值。
- NSPredicate *namesBeginningWithLetterPredicate = [NSPredicate predicateWithFormat:@"(firstName BEGINSWITH[cd] $letter) OR (lastName BEGINSWITH[cd] $letter)"];
-
- NSLog(@"'A' Names: %@", [people filteredArrayUsingPredicate:[namesBeginningWithLetterPredicate predicateWithSubstitutionVariables:@{@"letter": @"A"}]]);
基本比較
=, ==:左邊的表達式和右邊的表達式相等。
>=, =>:左邊的表達式大於或者等於右邊的表達式。
<=, =<:左邊的表達式小於等於右邊的表達式。
>:左邊的表達式大於右邊的表達式。
<:左邊的表達式小於右邊的表達式。
!=, <>:左邊的表達式不等於右邊的表達式。
BETWEEN:左邊的表達式等於右邊的表達式的值或者介於它們之間。右邊是一個有兩個指定上限和下限的數值的數列(指定順序的數列)。好比,1 BETWEEN { 0 , 33 },或者$INPUT BETWEEN { $LOWER, $UPPER }。
基本複合謂詞
AND, &&:邏輯與.
OR, ||:邏輯或.
NOT, !:邏輯非.
字符串比較
字符串比較在默認的狀況下是區分大小寫和音調的。你能夠在方括號中用關鍵字符c和d來修改操做符以相應的指定不區分大小寫和變音符號,好比firstname BEGINSWITH[cd] $FIRST_NAME。
BEGINSWITH:左邊的表達式以右邊的表達式做爲開始。
CONTAINS:左邊的表達式包含右邊的表達式。
ENDSWITH:左邊的表達式以右邊的表達式做爲結束。
LIKE:左邊的表達式等於右邊的表達式:?和*可做爲通配符,其中?匹配1個字符,*匹配0個或者多個字符。
合計操做
關係操做
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:結果始終爲假的謂詞。
NSCompoundPredicate
咱們見過與&或被用在謂詞格式字符串中以建立複合謂詞。然而,咱們也能夠用NSCompoundPredicate來完成一樣的工做。
例如,下列謂詞是相等的:
- [NSCompoundPredicate andPredicateWithSubpredicates:@[[NSPredicate predicateWithFormat:@"age > 25"], [NSPredicate predicateWithFormat:@"firstName = %@", @"Quentin"]]];
-
- [NSPredicate predicateWithFormat:@"(age > 25) AND (firstName = %@)", @"Quentin"];
雖然語法字符串文字更加容易輸入,可是在有的時候,你須要結合現有的謂詞。在那些狀況下,你可使用NSCompoundPredicate -andPredicateWithSubpredicates:&-orPredicateWithSubpredicates:。
NSComparisonPredicate
一樣的,若是你在讀過
上週的文章以後發現你使用了太多的NSExpression的話,NSComparisonPredicate能夠幫助你解決這個問題。
就像NSCompoundPredicate同樣,NSComparisonPredicate從子部件構建了一個NSPredicate-- 在這種狀況下,左側和右側都是NSExpression。 分析它的類的構造函數可讓咱們一窺NSPredicate的格式字符串是如何解析的:
- + (NSPredicate *)predicateWithLeftExpression:(NSExpression *)lhs
- rightExpression:(NSExpression *)rhs
- modifier:(NSComparisonPredicateModifier)modifier
- type:(NSPredicateOperatorType)type
- options:(NSUInteger)options
參數
lhs:左邊的表達式。
rhs:右邊的表達式。
modifier:應用的修改符。(ANY或者ALL)
type:謂詞運算符類型。
options:要應用的選項。沒有選項的話則爲0。
NSComparisonPredicate類型
- enum {
- NSLessThanPredicateOperatorType = 0,
- NSLessThanOrEqualToPredicateOperatorType,
- NSGreaterThanPredicateOperatorType,
- NSGreaterThanOrEqualToPredicateOperatorType,
- NSEqualToPredicateOperatorType,
- NSNotEqualToPredicateOperatorType,
- NSMatchesPredicateOperatorType,
- NSLikePredicateOperatorType,
- NSBeginsWithPredicateOperatorType,
- NSEndsWithPredicateOperatorType,
- NSInPredicateOperatorType,
- NSCustomSelectorPredicateOperatorType,
- NSContainsPredicateOperatorType,
- NSBetweenPredicateOperatorType
- };
- typedef NSUInteger NSPredicateOperatorType;
NSComparisonPredicate選項
NSCaseInsensitivePredicateOption:不區分大小寫的謂詞。你經過在謂詞格式字符串中加入後面帶有[c]的字符串操做(好比,"NeXT" like[c] "next")來表達這一選項。
NSDiacriticInsensitivePredicateOption:忽視發音符號的謂詞。你經過在謂詞格式字符串中加入後面帶有[d]的字符串操做(好比,"naïve" like[d] "naive")來表達這一選項。
NSNormalizedPredicateOption: 表示待比較的字符串已經被預處理了。這一選項取代了NSCaseInsensitivePredicateOption和 NSDiacriticInsensitivePredicateOption,旨在用做性能優化的選項。你能夠經過在謂詞格式字符串中加入後面帶有 [n]的字符串(好比,"WXYZlan" matches[n] ".lan")來表達這一選項。
NSLocaleSensitivePredicateOption: 代表要使用<,<=,=,=>,> 做爲比較的字符串應該使用區域識別的方式處理。你能夠經過在<,<=,=,=>,>其中之一的操做符後加入[l](比 如,"straße" >[l] "strasse")以便在謂詞格式字符串表達這一選項。
Block謂詞
最後,若是你實在不肯意學習NSPredicate的格式語法,你也能夠學學NSPredicate +predicateWithBlock:。
- NSPredicate *shortNamePredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
- return [[evaluatedObject firstName] length] <= 5;
- }];
-
- NSLog(@"Short Names: %@", [people filteredArrayUsingPredicate:shortNamePredicate]);
...好吧,雖然使用predicateWithBlock:是懶人的作法,但它也並非一無可取。
事實上,由於block能夠封裝任意的計算,因此有一個查詢類是沒法以NSPredicate格式字符串形式來表達的(好比對運行時被動態計算 的值的評估)。並且當同一件事情能夠用NSExpression結合自定義選擇器來完成時,block爲完成工做提供了一個方便的接口。
重要提示:由predicateWithBlock:生成的NSPredicate不能用於由SQLite存儲庫支持的Core Data數據的提取要求。
我知道我已經說過不少次了,但是NSPredicate真的是Cocoa的優點之一。其餘語言的第三方庫若是能有它一半的能力就已經很幸運了--更別提標準庫了。對於咱們這些應用和框架開發者來講,有它做爲標準組件使得咱們在處理數據時有了很大的優點。
和NSExpression同樣,NSPredicate一直在提醒咱們Foundation有多麼好:它不只僅十分有用,它精緻的構架和設計也是咱們寫代碼時靈感的來源。