进一步阅读之前,可以先思考这个问题: 存在数组 @[@(90), @(31), @(65), @(78), @(76)],如何取出数组中的最大值或者最小值?

KVC 的集合操作符可使用键路径和操作运算作用于集合中的所有元素。本文将描述一些可用的集合操作。

KVC 的集合操作符实际上就是一些特殊的键路径,以参数的形式传递给 valueForKeyPath: 方法。集合操作是以 @* 开始的字符串, 也可理解为: KVC集合操作符允许在 valueForKeyPath: 方法中使用 key path 符号在一个集合中执行方法。无论什么时候你在 key path 中看见了@*,它都代表了一个特定的集合方法,其结果可以被返回或者链接,就像其他的 key path 一样。下图就是集合操作符的格式:

Operator key path format
Operator key path format

其中左边的键路径(keypathToCollection)指定了相对消息接收者的 NSArray 或者 NSSet,右边的键路径(keypathToProperty)指定了相对于集合内对象的键路径,集合操作作用于该键路径。

除了 @count 外的所有集合操作,都要求在集合操作右边有一键路径(keypathToProperty)。

集合操作返回的对象值决定于集合操作的类型:

示例数据

以下示例会用到一些数据,先看看示例数据。想象一个 Product 类和由 Product 类对象所组成的 products 数组:

1
2
3
4
5
@interface Product : NSObject
@property NSString *name;
@property CGFloat price;
@property NSDate *launchedOn;
@end

键-值 编码会在必要的时候把基本数据类型的数据自动装箱和拆箱到 NSNumber 或者 NSValue 中来确保一切工作正常。

products 数组中 Product 类对象所使用数据如下:

Name Price Launch Date
iPhone5 $199 September 21, 2012
iPhone5 $199 September 21, 2012
iPhone5 $199 September 21, 2012
iPad Mini $329 November 2, 2012
MacBook Pro $1699 June 11,2012
MacBook Pro $1699 June 11,2012
iMac $1299 November 2, 2012

还有另一个数组 products2,它内部元素也是 Product 类对象,数据如下:

Name Price Launch Date
iPhone5 $199 September 21, 2012
iPad Mini $329 November 2, 2012
MacBook Pro $1699 June 11,2012
iMac $1299 November 2, 2012
iMac $1299 November 2, 2012
iMac $1299 November 2, 2012

简单集合操作符

简单集合操作符作用于 array 或者 set 中相对于集合操作符右侧的属性。包括 @avg, @count, @max, @min, @sum.

  • @avg 操作符将集合中属性键路径所指对象转换为 double, 计算其平均值,返回该平均值的 NSNumber 对象。当均值为 nil 的时候,返回 0.
  • @count 操作符返回集合中对象总数的 NSNumber 对象。操作符右边没有键路径。
  • @max 操作符比较由操作符右边的键路径指定的属性值,并返回比较结果的最大值。最大值由指定的键路径所指对象的 compare: 方法决定,因此参加比较的对象必须支持和另一个对象的比较。如果右侧键路径所指对象值为 nil, 则忽略,不影响比较结果。
  • @min@max 一样,但是返回的是集合中的最小值。
  • @sum 返回右侧键路径指定的属性值的总和。每一个比较值都转换为 double,然后计算值的总和,最后返回总和值的 NSNumber 对象。如果右侧键路径所指对象值为 nil,则忽略。
1
2
3
4
5
[products valueForKeyPath:@"@count"]; // 7
[products valueForKeyPath:@"@sum.price"]; // 5632.00
[products valueForKeyPath:@"@avg.price"]; // 804.5714285714286
[products valueForKeyPath:@"@max.price"]; // 1699.00
[products valueForKeyPath:@"@min.launchedOn"]; // June 11, 2012

Pro提示:你可以简单的通过把 self 作为操作符后面的 key path 来获取一个由 NSNumber 组成的数组或者集合的总值,例如对于数组 @[@(1), @(2), @(3)] 可使用 valueForKeyPath:@"@max.self" 来获取最大值。

对象操作符

对象操作符包括 @distinctUnionOfObjects@unionOfObjects, 返回一个由操作符右边的 key path 所指定的对象属性组成的数组。其中 @distinctUnionOfObjects 会对数组去重,而 @unionOfObjects 不会。

1
2
NSArray *unionOfObjects = [products valueForKeyPath:@"@unionOfObjects.name"]; // 1. 
NSArray *distinctUnionObjects = [products valueForKeyPath:@"@distinctUnionOfObjects.name"]; //2.

unionOfObjects 中会包含 "iPhone5", "iPhone5","iPhone5", "iPad Mini", "MacBook Pro", "MacBook Pro", "iMac"

distinctUnionObjects 中会包含 "iPhone5", "iPad Mini", "MacBook Pro", "iMac", 相当于对 unionOfObjects 数组进行去重处理。

数组和集合操作符

数组和集合操作符作用对象是嵌套的集合,也就是说,是一个集合且其内部每个元素是一个集合。数组和集合操作符包括 @distinctUnionOfArrays@unionOfArrays@distinctUnionOfSets:

  • @distinctUnionOfArrays / @unionOfArrays 返回一个数组,其中包含这个集合中每个数组对于这个操作符右面指定的 key path 进行操作之后的值。 distinct 版本会移除重复的值。
  • @distinctUnionOfSets@distinctUnionOfArrays 差不多, 但是它期望的是一个包含着 NSSet 对象的 NSSet ,并且会返回一个 NSSet 对象。因为集合不能包含重复的值,所以它只有 distinct 操作。

假定有个 totalProducts 数组,其包含两个数组 productsproducts2,以下代码片段展示了 totalProducts 的创建。

1
2
3
totalProducts = [NSMutableArray array];
[totalProducts addObject:products];
[totalProducts addObject:products2];

其中 productsproducts2 中元素都是 Product 类对象,其内部数据使用的是示例数据的数据内容。

对于以下操作:

1
2
NSArray *distinctUnionOfArrays = [totalProducts valueForKeyPath:@"@distinctUnionOfArrays.name"];
NSArray *unionOfArrays = [totalProducts valueForKeyPath:@"@unionOfArrays.name"];

那么 unionOfArrays 中包含 "iPhone5", "iPhone5", "iPhone5", "iPad Mini", "MacBook Pro", "MacBook Pro", "iMac", "iPhone5", "iPad Mini", "MacBook Pro", "iMac", "iMac", "iMac" 共 13 个元素。而 distinctUnionOfArrays 中仅包含 "iPhone5", "iPad Mini", "MacBook Pro", "iMac" 共 4 个元素。

注意: 如果操作符右侧 key path 指定的对象为 nil,那么返回的数组中会包含 NSNull 对象.

参考:


如果觉得本文对你有帮助,就请用微信打赏我吧^_^