每當涉及查詢或者整理資訊時,Cocoa總是其他标準庫羨慕的對象。通過使用NSPredicate, NSSortDescriptor
,以及偶爾使用NSFetchRequest,即使是最複雜的資料任務也可以被簡化成為幾行極其容易讀懂的代碼。
現在,NSHipster們無疑已經熟悉NSPredicate 了,不過如果我們更進一步看看NSPredicate,我們會發現NSPredicate其實是由更小的部分而組成:兩個NSExpression(一個左手值和一個右手值),和一個運算符相比較(比如
<
, IN
LIKE
等等)。 
+predicateWithFormat:
來使用NSPredicate,NSExpression是一個相對難懂的類。真可惜啊,因為NSExpression本身的功能非常強大。是以,親愛的讀者,請允許我來表達我對NSExpression深深的尊重和着迷: 評估數學
關于NSExpression你所要知道的第一件事就是它的主要目的是減少表達。如果你思考一下評估NSPredicate的過程,你會發現它有兩個表達和一個比較符号,是以我們需要将兩個表達簡化為運算符可以處理的表達–非常像編譯一行代碼的過程。
這就是我們要學習的NSExpression的第一招: 做數學題。
NSExpression *expression = [NSExpression expressionWithFormat:@"4 + 5 - 2**3"];
id value = [expression expressionValueWithObject:nil context:nil]; // => 1
這并不是
Wolfram Alpha,但是如果加入評估數學表達式對于你的應用很有用的話,那麼…你就可以使用NSExpression。
函數
我們僅僅觸及了NSExpression的表面。覺得一台電腦僅僅做國小數學不怎麼厲害?那高中的統計學怎麼樣?
NSArray *numbers = @[@1, @2, @3, @4, @4, @5, @9, @11];
NSExpression *expression = [NSExpression expressionForFunction:@"stddev:" arguments:@[[NSExpression expressionForConstantValue:numbers]]];
id value = [expression expressionValueWithObject:nil context:nil]; // => 3.21859...
NSExpression 函數以給定數目的子表達式作為參數。比如,在上述例子中,要得到集合的标準差,數列中的數字要被+expressionForConstantValue:封裝。雖然隻是一個小小的不便(它最終卻能使得NSExpression變得極其靈活),卻足以使第一次嘗試它的人絆倒。
如果你覺得
鍵值編碼簡單集合運算符(@avg,@sum等等)不夠用,也許NSExpression的自帶的統計,算術和位運算功能能激起你的興趣。
要注意的是:根據
Apple的NSExpression文檔中的表格,很明顯,Mac OS X & iOS的功能可用性之間沒有重疊。看起來最近的iOS版本的确支援如stddev之類的函數,但這些變化并沒有顯示在頭檔案或者文檔裡。如果你注意到任何變化,請以
pull request的形式告訴我,不勝感激。
統計
average:
sum:
count:
min:
max:
median:
mode:
stddev:
基本運算
這些函數需要用兩個NSExpression對象來表達數字。
add:to:
from:subtract:
multiply:by:
divide:by:
modulus:by:
abs:
進階運算
sqrt:
log:
ln:
raise:toPower:
exp:
邊界函數
ceiling: – (不小于數組中的值的最小積分值)
trunc: – (最接近但不大于數組中的值的積分值)
與math.h函數類似的函數
ceiling非常容易和ceil(3)混淆。ceiling作用于數字數組,而ceil(3)作用于一個double值(且它并沒對應的内置NSExpression函數)。floor:在這裡的作用和floor(3)一樣。
随機函數
兩個變量–一個帶參數,一個不帶參數。不帶參數時,random傳回rand(3)的等值,而random:則從NSExpression的數字數組中取任意元素。
random
random:
二進制運算
bitwiseAnd:with:
bitwiseOr:with:
bitwiseXor:with:
leftshift:by:
rightshift:by:
onesComplement:
日期函數
now
字元串函數
lowercase:
uppercase:
空操作
noindex:
自定義函數
除了這些内置的函數,你也可以在NSExpression中調用自定義函數。由Dave DeLong所撰寫的這篇文章(
Using custom functions with NSExpression) 詳述了這個過程。
首先,在類别中定義一個對應的函數:
@interface NSNumber (Factorial)
- (NSNumber *)factorial;
@end
@implementation NSNumber (Factorial)
- (NSNumber *)factorial {
return @(tgamma([self doubleValue] + 1));
}
@end
然後,這樣使用函數(
+expressionWithFormat:
中的
FUNCTION()
宏是構造
-expressionForFunction:
等等的過程的簡寫。):
NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(4.2, 'factorial')"];
id value = [expression expressionValueWithObject:nil context:nil]; // 32.578...
這樣的優勢在于, 通過直接調用-factorial,我們可以調用NSPredicate查詢中的函數。比如,我們可以定義一個location:withinRadius:方法來輕松的查詢使用者目前位置附近的管理對象。
正如Dave在他的文章中所提到的那樣,這些用例十分邊緣化,但它們肯定可以成為你的保留節目中有趣的技巧。