收集運算子
集合運算子可用於 KVC 金鑰路徑,以對集合型別屬性(即 NSArray
,NSSet
等)執行操作。例如,要執行的常見操作是計算集合中的物件。要實現此目的,請使用 @count
集合運算子 :
self.array = @[@5, @4, @3, @2, @1];
NSNumber *count = [self.array valueForKeyPath:@"@count"];
NSNumber *countAlt = [self valueForKeyPath:@"array.@count"];
// count == countAlt == 5
雖然這是完全多餘的,這裡(我們可以只訪問 count
屬性),它可以是偶爾有用,雖然它很少是必要的。然而,有一些更有用的收集運算元,即 @max
,@min
,@sum
,@avg
和 @unionOf
系列。需要注意的是這些運算子是非常重要的還需要一個單獨的關鍵路徑如下操作才能正常工作。以下是它們的列表以及它們使用的資料型別:
操作者 | 資料型別 |
---|---|
@count |
(沒有) |
@max |
NSNumber ,NSDate ,int (及相關)等 |
@min |
NSNumber ,NSDate ,int (及相關)等 |
@sum |
NSNumber ,int (及相關),double (及相關)等 |
@avg |
NSNumber ,int (及相關),double (及相關)等 |
@unionOfObjects |
NSArray ,NSSet 等 |
@distinctUnionOfObjects |
NSArray ,NSSet 等 |
@unionOfArrays |
NSArray<NSArray*> |
@distinctUnionOfArrays |
NSArray<NSArray*> |
@distinctUnionOfSets |
NSSet<NSSet*> |
@max
和 @min
將分別返回集合中物件屬性的最高值或最低值。例如,檢視以下程式碼:
// `Point` class used in our collection
@interface Point : NSObject
@property NSInteger x, y;
+ (instancetype)pointWithX:(NSInteger)x y:(NSInteger)y;
@end
...
self.points = @[[Point pointWithX:0 y:0],
[Point pointWithX:1 y:-1],
[Point pointWithX:5 y:-6],
[Point pointWithX:3 y:0],
[Point pointWithX:8 y:-4],
];
NSNumber *maxX = [self valueForKeyPath:@"points.@max.x"];
NSNumber *minX = [self valueForKeyPath:@"points.@min.x"];
NSNumber *maxY = [self valueForKeyPath:@"points.@max.y"];
NSNumber *minY = [self valueForKeyPath:@"points.@min.y"];
NSArray<NSNumber*> *boundsOfAllPoints = @[maxX, minX, maxY, minY];
...
在僅僅 4 行程式碼和純基礎中,憑藉 Key-Value Coding 集合運算子的強大功能,我們能夠提取一個封裝陣列中所有點的矩形。
重要的是要注意這些比較是通過在物件上呼叫 compare:
方法進行的,因此如果你想讓自己的類與這些運算子相容,則必須實現此方法。
正如你可能猜到的那樣,@sum
將新增屬性的所有值。
@interface Expense : NSObject
@property NSNumber *price;
+ (instancetype)expenseWithPrice:(NSNumber *)price;
@end
...
self.expenses = @[[Expense expenseWithPrice:@1.50],
[Expense expenseWithPrice:@9.99],
[Expense expenseWithPrice:@2.78],
[Expense expenseWithPrice:@9.99],
[Expense expenseWithPrice:@24.95]
];
NSNumber *totalExpenses = [self valueForKeyPath:@"expenses.@sum.price"];
在這裡,我們使用 @sum
來查詢陣列中所有費用的總價。如果我們想要找到我們為每筆費用支付的平均價格,我們可以使用 @avg
:
NSNumber *averagePrice = [self valueForKeyPath:@"expenses.@avg.price"];
最後,還有 @unionOf
家族。這個系列中有五個不同的運算子,但它們的工作方式大致相同,每個運算子之間的差異很小。首先,@unionOfObjects
將返回陣列中物件屬性的陣列:
// See "expenses" array above
NSArray<NSNumber*> *allPrices = [self valueForKeyPath:
@"expenses.@unionOfObjects.price"];
// Equal to @[ @1.50, @9.99, @2.78, @9.99, @24.95 ]
@distinctUnionOfObjects
的功能與 @unionOfObjects
相同,但它會刪除重複項:
NSArray<NSNumber*> *differentPrices = [self valueForKeyPath:
@"expenses.@distinctUnionOfObjects.price"];
// Equal to @[ @1.50, @9.99, @2.78, @24.95 ]
最後,@unionOf
系列中的最後 3 個運算子將更深入一步並返回為雙重巢狀陣列中包含的屬性找到的值陣列:
NSArray<NSArray<Expense*,Expense*>*> *arrayOfArrays =
@[
@[ [Expense expenseWithPrice:@19.99],
[Expense expenseWithPrice:@14.95],
[Expense expenseWithPrice:@4.50],
[Expense expenseWithPrice:@19.99]
],
@[ [Expense expenseWithPrice:@3.75],
[Expense expenseWithPrice:@14.95]
]
];
// @unionOfArrays
NSArray<NSNumber*> allPrices = [arrayOfArrays valueForKeyPath:
@"@unionOfArrays.price"];
// Equal to @[ @19.99, @14.95, @4.50, @19.99, @3.75, @14.95 ];
// @distinctUnionOfArrays
NSArray<NSNumber*> allPrices = [arrayOfArrays valueForKeyPath:
@"@distinctUnionOfArrays.price"];
// Equal to @[ @19.99, @14.95, @4.50, @3.75 ];
這個例子中缺少的是 @distinctUnionOfSets
,但是它的功能與 @distinctUnionOfArrays
完全相同,但是使用並返回 NSSet
s(沒有非 distinct
版本,因為在一個集合中,每個物件必須是不同的)。
就是這樣! 如果使用正確,集合運算子可以非常強大,並且可以幫助避免不必要地迴圈遍歷內容。
最後一點:你還可以在 NSNumber
s 的陣列上使用標準集合運算子(無需額外的屬性訪問)。為此,你可以訪問僅返回物件的 self
偽屬性:
NSArray<NSNumber*> *numbers = @[@0, @1, @5, @27, @1337, @2048];
NSNumber *largest = [numbers valueForKeyPath:@"@max.self"];
NSNumber *smallest = [numbers valueForKeyPath:@"@min.self"];
NSNumber *total = [numbers valueForKeyPath:@"@sum.self"];
NSNumber *average = [numbers valueForKeyPath:@"@avg.self"];